261 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
		
		
			
		
	
	
			261 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
|   | --- | ||
|  | id: 5a24c314108439a4d4036142 | ||
|  | title: 最初に state をローカルに管理する | ||
|  | challengeType: 6 | ||
|  | forumTopicId: 301431 | ||
|  | dashedName: manage-state-locally-first | ||
|  | --- | ||
|  | 
 | ||
|  | # --description--
 | ||
|  | 
 | ||
|  | ここでは、`DisplayMessages` コンポーネントの作成を完了させます。 | ||
|  | 
 | ||
|  | # --instructions--
 | ||
|  | 
 | ||
|  | まず、`render()` メソッドで、コンポーネントに `input` 要素、`button` 要素、および `ul` 要素をレンダーさせてください。 `input` 要素が変更されたときは、`handleChange()` メソッドをトリガーする必要があります。 また、`input` 要素で、コンポーネントの state にある `input` の値をレンダーしてください。 `button` 要素がクリックされたときは、`submitMessage()` メソッドをトリガーする必要があります。 | ||
|  | 
 | ||
|  | 次に、2 つのメソッドを記述してください。 `handleChange()` メソッドでは、`input` を、ユーザーが入力している内容に更新してください。 `submitMessage()` メソッドでは、現在のメッセージ (`input` に格納されています) をローカル state の `messages` 配列に結合し、`input` の値をクリアしてください。 | ||
|  | 
 | ||
|  | 最後に、`ul` を使用して `messages` の配列をマップし、`li` 要素のリストとして画面にレンダーしてください。 | ||
|  | 
 | ||
|  | # --hints--
 | ||
|  | 
 | ||
|  | `DisplayMessages` コンポーネントを初期化し、state を `{ input: "", messages: [] }` にします。 | ||
|  | 
 | ||
|  | ```js | ||
|  | assert( | ||
|  |   (function () { | ||
|  |     const mockedComponent = Enzyme.mount(React.createElement(DisplayMessages)); | ||
|  |     const initialState = mockedComponent.state(); | ||
|  |     return ( | ||
|  |       typeof initialState === 'object' && | ||
|  |       initialState.input === '' && | ||
|  |       initialState.messages.length === 0 | ||
|  |     ); | ||
|  |   })() | ||
|  | ); | ||
|  | ``` | ||
|  | 
 | ||
|  | `DisplayMessages` コンポーネントで、`h2` 要素、`button` 要素、 `ul` 要素、および `li` 要素を子として含む `div` をレンダーします。 | ||
|  | 
 | ||
|  | ```js | ||
|  | async () => { | ||
|  |   const mockedComponent = Enzyme.mount(React.createElement(DisplayMessages)); | ||
|  |   const waitForIt = (fn) => | ||
|  |     new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 100)); | ||
|  |   const state = () => { | ||
|  |     mockedComponent.setState({ messages: ['__TEST__MESSAGE'] }); | ||
|  |     return waitForIt(() => mockedComponent); | ||
|  |   }; | ||
|  |   const updated = await state(); | ||
|  |   assert( | ||
|  |     updated.find('div').length === 1 && | ||
|  |       updated.find('h2').length === 1 && | ||
|  |       updated.find('button').length === 1 && | ||
|  |       updated.find('ul').length === 1 && | ||
|  |       updated.find('li').length > 0 | ||
|  |   ); | ||
|  | }; | ||
|  | ``` | ||
|  | 
 | ||
|  | `messages` 配列で `.map` を使用します。 | ||
|  | 
 | ||
|  | ```js | ||
|  | assert(code.match(/this\.state\.messages\.map/g)); | ||
|  | ``` | ||
|  | 
 | ||
|  | `input` 要素で、ローカルの state にある `input` の値をレンダーします。 | ||
|  | 
 | ||
|  | ```js | ||
|  | async () => { | ||
|  |   const mockedComponent = Enzyme.mount(React.createElement(DisplayMessages)); | ||
|  |   const waitForIt = (fn) => | ||
|  |     new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 100)); | ||
|  |   const causeChange = (c, v) => | ||
|  |     c.find('input').simulate('change', { target: { value: v } }); | ||
|  |   const testValue = '__TEST__EVENT__INPUT'; | ||
|  |   const changed = () => { | ||
|  |     causeChange(mockedComponent, testValue); | ||
|  |     return waitForIt(() => mockedComponent); | ||
|  |   }; | ||
|  |   const updated = await changed(); | ||
|  |   assert(updated.find('input').props().value === testValue); | ||
|  | }; | ||
|  | ``` | ||
|  | 
 | ||
|  | メソッド `handleChange` の呼び出しで、`input` の値を現在の入力に更新します。 | ||
|  | 
 | ||
|  | ```js | ||
|  | async () => { | ||
|  |   const mockedComponent = Enzyme.mount(React.createElement(DisplayMessages)); | ||
|  |   const waitForIt = (fn) => | ||
|  |     new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 100)); | ||
|  |   const causeChange = (c, v) => | ||
|  |     c.find('input').simulate('change', { target: { value: v } }); | ||
|  |   const initialState = mockedComponent.state(); | ||
|  |   const testMessage = '__TEST__EVENT__MESSAGE__'; | ||
|  |   const changed = () => { | ||
|  |     causeChange(mockedComponent, testMessage); | ||
|  |     return waitForIt(() => mockedComponent); | ||
|  |   }; | ||
|  |   const afterInput = await changed(); | ||
|  |   assert( | ||
|  |     initialState.input === '' && | ||
|  |       afterInput.state().input === '__TEST__EVENT__MESSAGE__' | ||
|  |   ); | ||
|  | }; | ||
|  | ``` | ||
|  | 
 | ||
|  | `Add message` ボタンのクリックで、メソッド `submitMessage` を呼び出します。このメソッドは、現在の `input` を state 内の `messages` 配列に追加します。 | ||
|  | 
 | ||
|  | ```js | ||
|  | async () => { | ||
|  |   const mockedComponent = Enzyme.mount(React.createElement(DisplayMessages)); | ||
|  |   const waitForIt = (fn) => | ||
|  |     new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 100)); | ||
|  |   const causeChange = (c, v) => | ||
|  |     c.find('input').simulate('change', { target: { value: v } }); | ||
|  |   const initialState = mockedComponent.state(); | ||
|  |   const testMessage_1 = '__FIRST__MESSAGE__'; | ||
|  |   const firstChange = () => { | ||
|  |     causeChange(mockedComponent, testMessage_1); | ||
|  |     return waitForIt(() => mockedComponent); | ||
|  |   }; | ||
|  |   const firstResult = await firstChange(); | ||
|  |   const firstSubmit = () => { | ||
|  |     mockedComponent.find('button').simulate('click'); | ||
|  |     return waitForIt(() => mockedComponent); | ||
|  |   }; | ||
|  |   const afterSubmit_1 = await firstSubmit(); | ||
|  |   const submitState_1 = afterSubmit_1.state(); | ||
|  |   const testMessage_2 = '__SECOND__MESSAGE__'; | ||
|  |   const secondChange = () => { | ||
|  |     causeChange(mockedComponent, testMessage_2); | ||
|  |     return waitForIt(() => mockedComponent); | ||
|  |   }; | ||
|  |   const secondResult = await secondChange(); | ||
|  |   const secondSubmit = () => { | ||
|  |     mockedComponent.find('button').simulate('click'); | ||
|  |     return waitForIt(() => mockedComponent); | ||
|  |   }; | ||
|  |   const afterSubmit_2 = await secondSubmit(); | ||
|  |   const submitState_2 = afterSubmit_2.state(); | ||
|  |   assert( | ||
|  |     initialState.messages.length === 0 && | ||
|  |       submitState_1.messages.length === 1 && | ||
|  |       submitState_2.messages.length === 2 && | ||
|  |       submitState_2.messages[1] === testMessage_2 | ||
|  |   ); | ||
|  | }; | ||
|  | ``` | ||
|  | 
 | ||
|  | `submitMessage` メソッドで、現在の入力をクリアします。 | ||
|  | 
 | ||
|  | ```js | ||
|  | async () => { | ||
|  |   const mockedComponent = Enzyme.mount(React.createElement(DisplayMessages)); | ||
|  |   const waitForIt = (fn) => | ||
|  |     new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 100)); | ||
|  |   const causeChange = (c, v) => | ||
|  |     c.find('input').simulate('change', { target: { value: v } }); | ||
|  |   const initialState = mockedComponent.state(); | ||
|  |   const testMessage = '__FIRST__MESSAGE__'; | ||
|  |   const firstChange = () => { | ||
|  |     causeChange(mockedComponent, testMessage); | ||
|  |     return waitForIt(() => mockedComponent); | ||
|  |   }; | ||
|  |   const firstResult = await firstChange(); | ||
|  |   const firstState = firstResult.state(); | ||
|  |   const firstSubmit = () => { | ||
|  |     mockedComponent.find('button').simulate('click'); | ||
|  |     return waitForIt(() => mockedComponent); | ||
|  |   }; | ||
|  |   const afterSubmit = await firstSubmit(); | ||
|  |   const submitState = afterSubmit.state(); | ||
|  |   assert(firstState.input === testMessage && submitState.input === ''); | ||
|  | }; | ||
|  | ``` | ||
|  | 
 | ||
|  | # --seed--
 | ||
|  | 
 | ||
|  | ## --after-user-code--
 | ||
|  | 
 | ||
|  | ```jsx | ||
|  | ReactDOM.render(<DisplayMessages />, document.getElementById('root')) | ||
|  | ``` | ||
|  | 
 | ||
|  | ## --seed-contents--
 | ||
|  | 
 | ||
|  | ```jsx | ||
|  | class DisplayMessages extends React.Component { | ||
|  |   constructor(props) { | ||
|  |     super(props); | ||
|  |     this.state = { | ||
|  |       input: '', | ||
|  |       messages: [] | ||
|  |     } | ||
|  |   } | ||
|  |   // Add handleChange() and submitMessage() methods here | ||
|  | 
 | ||
|  |   render() { | ||
|  |     return ( | ||
|  |       <div> | ||
|  |         <h2>Type in a new Message:</h2> | ||
|  |         { /* Render an input, button, and ul below this line */ } | ||
|  | 
 | ||
|  |         { /* Change code above this line */ } | ||
|  |       </div> | ||
|  |     ); | ||
|  |   } | ||
|  | }; | ||
|  | ``` | ||
|  | 
 | ||
|  | # --solutions--
 | ||
|  | 
 | ||
|  | ```jsx | ||
|  | class DisplayMessages extends React.Component { | ||
|  |   constructor(props) { | ||
|  |     super(props); | ||
|  |     this.state = { | ||
|  |       input: '', | ||
|  |       messages: [] | ||
|  |     } | ||
|  |  this.handleChange = this.handleChange.bind(this); | ||
|  |    this.submitMessage = this.submitMessage.bind(this); | ||
|  |  } | ||
|  |   handleChange(event) { | ||
|  |     this.setState({ | ||
|  |       input: event.target.value | ||
|  |     }); | ||
|  |   } | ||
|  |   submitMessage() { | ||
|  |     this.setState((state) => { | ||
|  |       const currentMessage = state.input; | ||
|  |       return { | ||
|  |         input: '', | ||
|  |         messages: state.messages.concat(currentMessage) | ||
|  |       };   | ||
|  |     }); | ||
|  |   } | ||
|  |   render() { | ||
|  |     return ( | ||
|  |       <div> | ||
|  |         <h2>Type in a new Message:</h2> | ||
|  |         <input | ||
|  |           value={this.state.input} | ||
|  |           onChange={this.handleChange}/><br/> | ||
|  |         <button onClick={this.submitMessage}>Submit</button> | ||
|  |         <ul> | ||
|  |           {this.state.messages.map( (message, idx) => { | ||
|  |               return ( | ||
|  |                  <li key={idx}>{message}</li> | ||
|  |               ) | ||
|  |             }) | ||
|  |           } | ||
|  |         </ul> | ||
|  |       </div> | ||
|  |     ); | ||
|  |   } | ||
|  | }; | ||
|  | ``` |