177 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
		
		
			
		
	
	
			177 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
|   | --- | ||
|  | id: 5a24c314108439a4d4036189 | ||
|  | title: コンポーネントの状態に基づいてインライン CSS を条件付きで変更する | ||
|  | challengeType: 6 | ||
|  | forumTopicId: 301380 | ||
|  | dashedName: change-inline-css-conditionally-based-on-component-state | ||
|  | --- | ||
|  | 
 | ||
|  | # --description--
 | ||
|  | 
 | ||
|  | ここまで、条件付きレンダーとインラインスタイルの使用について、いくつかの応用例を紹介しました。 これら両方のトピックを組み合わせた例をもう一つ紹介します。 React コンポーネントの状態に基づいて条件付きで CSS をレンダーすることもできます。 それには、条件をチェックし、条件が満たされている場合は、render メソッド内の JSX 要素に割り当てられているスタイルオブジェクトを変更します。 | ||
|  | 
 | ||
|  | この方法は、DOM 要素を直接変更してスタイルを適用するという従来のアプローチと比べて、かなり大きな変更となるため、理解することが重要です (たとえば jQuery ではごく一般的です)。 このアプローチでは、要素がいつ変更されたのかを追跡する必要があり、実際の操作を直接処理する必要もあります。 変更の追跡が困難になり、UI が予測できなくなる可能性があります。 条件に基づいてスタイルオブジェクトを設定するときは、UI をアプリケーションの状態の関数としてどのように表示するかを記述します。 情報の流れは明確で、一方向にしか流れません。 React でアプリケーションを記述するときは、こうした方法が適切です。 | ||
|  | 
 | ||
|  | # --instructions--
 | ||
|  | 
 | ||
|  | コードエディターに、スタイル付きの境界線を持つ、シンプルな制御された入力コンポーネントがあります。 ユーザーが入力ボックスに 15 文字を超えるテキストを入力した場合に、この境界線を赤色に変更する必要があります。 このことをチェックする条件を追加し、条件が有効な場合に入力の境界線のスタイルを `3px solid red` に設定してください。 入力欄にテキストを入力して試すことができます。 | ||
|  | 
 | ||
|  | # --hints--
 | ||
|  | 
 | ||
|  | `GateKeeper` コンポーネントで `div` 要素をレンダーします。 | ||
|  | 
 | ||
|  | ```js | ||
|  | assert( | ||
|  |   (function () { | ||
|  |     const mockedComponent = Enzyme.mount(React.createElement(GateKeeper)); | ||
|  |     return mockedComponent.find('div').length === 1; | ||
|  |   })() | ||
|  | ); | ||
|  | ``` | ||
|  | 
 | ||
|  | `GateKeeper` コンポーネントを初期化し、状態のキー `input` に空文字列を設定します。 | ||
|  | 
 | ||
|  | ```js | ||
|  | assert( | ||
|  |   (function () { | ||
|  |     const mockedComponent = Enzyme.mount(React.createElement(GateKeeper)); | ||
|  |     return mockedComponent.state().input === ''; | ||
|  |   })() | ||
|  | ); | ||
|  | ``` | ||
|  | 
 | ||
|  | `GateKeeper` コンポーネントで `h3` タグと `input` タグをレンダーします。 | ||
|  | 
 | ||
|  | ```js | ||
|  | assert( | ||
|  |   (function () { | ||
|  |     const mockedComponent = Enzyme.mount(React.createElement(GateKeeper)); | ||
|  |     return ( | ||
|  |       mockedComponent.find('h3').length === 1 && | ||
|  |       mockedComponent.find('input').length === 1 | ||
|  |     ); | ||
|  |   })() | ||
|  | ); | ||
|  | ``` | ||
|  | 
 | ||
|  | `input` タグの `border` プロパティに、初期状態でスタイル `1px solid black` を設定します。 | ||
|  | 
 | ||
|  | ```js | ||
|  | assert( | ||
|  |   (function () { | ||
|  |     const mockedComponent = Enzyme.mount(React.createElement(GateKeeper)); | ||
|  |     return ( | ||
|  |       mockedComponent.find('input').props().style.border === '1px solid black' | ||
|  |     ); | ||
|  |   })() | ||
|  | ); | ||
|  | ``` | ||
|  | 
 | ||
|  | 状態の入力値が 15 文字を超える場合は、`input` タグの境界線のスタイルを `3px solid red` に設定します。 | ||
|  | 
 | ||
|  | ```js | ||
|  | async () => { | ||
|  |   const waitForIt = (fn) => | ||
|  |     new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 100)); | ||
|  |   const mockedComponent = Enzyme.mount(React.createElement(GateKeeper)); | ||
|  |   const simulateChange = (el, value) => | ||
|  |     el.simulate('change', { target: { value } }); | ||
|  |   let initialStyle = mockedComponent.find('input').props().style.border; | ||
|  |   const state_1 = () => { | ||
|  |     mockedComponent.setState({ input: 'this is 15 char' }); | ||
|  |     return waitForIt(() => mockedComponent.find('input').props().style.border); | ||
|  |   }; | ||
|  |   const state_2 = () => { | ||
|  |     mockedComponent.setState({ | ||
|  |       input: 'A very long string longer than 15 characters.' | ||
|  |     }); | ||
|  |     return waitForIt(() => mockedComponent.find('input').props().style.border); | ||
|  |   }; | ||
|  |   const style_1 = await state_1(); | ||
|  |   const style_2 = await state_2(); | ||
|  |   assert( | ||
|  |     initialStyle === '1px solid black' && | ||
|  |       style_1 === '1px solid black' && | ||
|  |       style_2 === '3px solid red' | ||
|  |   ); | ||
|  | }; | ||
|  | ``` | ||
|  | 
 | ||
|  | # --seed--
 | ||
|  | 
 | ||
|  | ## --after-user-code--
 | ||
|  | 
 | ||
|  | ```jsx | ||
|  | ReactDOM.render(<GateKeeper />, document.getElementById('root')) | ||
|  | ``` | ||
|  | 
 | ||
|  | ## --seed-contents--
 | ||
|  | 
 | ||
|  | ```jsx | ||
|  | class GateKeeper extends React.Component { | ||
|  |   constructor(props) { | ||
|  |     super(props); | ||
|  |     this.state = { | ||
|  |       input: '' | ||
|  |     }; | ||
|  |     this.handleChange = this.handleChange.bind(this); | ||
|  |   } | ||
|  |   handleChange(event) { | ||
|  |     this.setState({ input: event.target.value }) | ||
|  |   } | ||
|  |   render() { | ||
|  |     let inputStyle = { | ||
|  |       border: '1px solid black' | ||
|  |     }; | ||
|  |     // Change code below this line | ||
|  | 
 | ||
|  |     // Change code above this line | ||
|  |     return ( | ||
|  |       <div> | ||
|  |         <h3>Don't Type Too Much:</h3> | ||
|  |         <input | ||
|  |           type="text" | ||
|  |           style={inputStyle} | ||
|  |           value={this.state.input} | ||
|  |           onChange={this.handleChange} /> | ||
|  |       </div> | ||
|  |     ); | ||
|  |   } | ||
|  | }; | ||
|  | ``` | ||
|  | 
 | ||
|  | # --solutions--
 | ||
|  | 
 | ||
|  | ```jsx | ||
|  | class GateKeeper extends React.Component { | ||
|  |   constructor(props) { | ||
|  |     super(props); | ||
|  |     this.state = { | ||
|  |       input: '' | ||
|  |     }; | ||
|  |     this.handleChange = this.handleChange.bind(this); | ||
|  |   } | ||
|  |   handleChange(event) { | ||
|  |     this.setState({ input: event.target.value }) | ||
|  |   } | ||
|  |   render() { | ||
|  |     let inputStyle = { | ||
|  |       border: '1px solid black' | ||
|  |     }; | ||
|  |     if (this.state.input.length > 15) { | ||
|  |       inputStyle.border = '3px solid red'; | ||
|  |     }; | ||
|  |     return ( | ||
|  |       <div> | ||
|  |         <h3>Don't Type Too Much:</h3> | ||
|  |         <input | ||
|  |           type="text" | ||
|  |           style={inputStyle} | ||
|  |           value={this.state.input} | ||
|  |           onChange={this.handleChange} /> | ||
|  |       </div> | ||
|  |     ); | ||
|  |   } | ||
|  | }; | ||
|  | ``` |