210 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
		
		
			
		
	
	
			210 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
|   | --- | ||
|  | id: 5a24c314108439a4d4036179 | ||
|  | title: Create a Controlled Form | ||
|  | challengeType: 6 | ||
|  | forumTopicId: 301384 | ||
|  | dashedName: create-a-controlled-form | ||
|  | --- | ||
|  | 
 | ||
|  | # --description--
 | ||
|  | 
 | ||
|  | The last challenge showed that React can control the internal state for certain elements like `input` and `textarea`, which makes them controlled components. This applies to other form elements as well, including the regular HTML `form` element. | ||
|  | 
 | ||
|  | # --instructions--
 | ||
|  | 
 | ||
|  | The `MyForm` component is set up with an empty `form` with a submit handler. The submit handler will be called when the form is submitted. | ||
|  | 
 | ||
|  | We've added a button which submits the form. You can see it has the `type` set to `submit` indicating it is the button controlling the form. Add the `input` element in the `form` and set its `value` and `onChange()` attributes like the last challenge. You should then complete the `handleSubmit` method so that it sets the component state property `submit` to the current input value in the local `state`. | ||
|  | 
 | ||
|  | **Note:** You also must call `event.preventDefault()` in the submit handler, to prevent the default form submit behavior which will refresh the web page. | ||
|  | 
 | ||
|  | Finally, create an `h1` tag after the `form` which renders the `submit` value from the component's `state`. You can then type in the form and click the button (or press enter), and you should see your input rendered to the page. | ||
|  | 
 | ||
|  | # --hints--
 | ||
|  | 
 | ||
|  | `MyForm` should return a `div` element which contains a `form` and an `h1` tag. The form should include an `input` and a `button`. | ||
|  | 
 | ||
|  | ```js | ||
|  | assert( | ||
|  |   (() => { | ||
|  |     const mockedComponent = Enzyme.mount(React.createElement(MyForm)); | ||
|  |     return ( | ||
|  |       mockedComponent.find('div').children().find('form').length === 1 && | ||
|  |       mockedComponent.find('div').children().find('h1').length === 1 && | ||
|  |       mockedComponent.find('form').children().find('input').length === 1 && | ||
|  |       mockedComponent.find('form').children().find('button').length === 1 | ||
|  |     ); | ||
|  |   })() | ||
|  | ); | ||
|  | ``` | ||
|  | 
 | ||
|  | The state of `MyForm` should initialize with `input` and `submit` properties, both set to empty strings. | ||
|  | 
 | ||
|  | ```js | ||
|  | assert( | ||
|  |   Enzyme.mount(React.createElement(MyForm)).state('input') === '' && | ||
|  |     Enzyme.mount(React.createElement(MyForm)).state('submit') === '' | ||
|  | ); | ||
|  | ``` | ||
|  | 
 | ||
|  | Typing in the `input` element should update the `input` property of the component's state. | ||
|  | 
 | ||
|  | ```js | ||
|  | (() => { | ||
|  |   const mockedComponent = Enzyme.mount(React.createElement(MyForm)); | ||
|  |   const _1 = () => { | ||
|  |     mockedComponent.setState({ input: '' }); | ||
|  |     return mockedComponent.state('input'); | ||
|  |   }; | ||
|  |   const _2 = () => { | ||
|  |     mockedComponent | ||
|  |       .find('input') | ||
|  |       .simulate('change', { target: { value: 'TestInput' } }); | ||
|  |     return { | ||
|  |       state: mockedComponent.state('input'), | ||
|  |       inputVal: mockedComponent.find('input').props().value | ||
|  |     }; | ||
|  |   }; | ||
|  |   const before = _1(); | ||
|  |   const after = _2(); | ||
|  |   assert( | ||
|  |     before === '' && | ||
|  |       after.state === 'TestInput' && | ||
|  |       after.inputVal === 'TestInput' | ||
|  |   ); | ||
|  | })(); | ||
|  | ``` | ||
|  | 
 | ||
|  | Submitting the form should run `handleSubmit` which should set the `submit` property in state equal to the current input. | ||
|  | 
 | ||
|  | ```js | ||
|  | (() => { | ||
|  |   const mockedComponent = Enzyme.mount(React.createElement(MyForm)); | ||
|  |   const _1 = () => { | ||
|  |     mockedComponent.setState({ input: '' }); | ||
|  |     mockedComponent.setState({ submit: '' }); | ||
|  |     mockedComponent | ||
|  |       .find('input') | ||
|  |       .simulate('change', { target: { value: 'SubmitInput' } }); | ||
|  |     return mockedComponent.state('submit'); | ||
|  |   }; | ||
|  |   const _2 = () => { | ||
|  |     mockedComponent.find('form').simulate('submit'); | ||
|  |     return mockedComponent.state('submit'); | ||
|  |   }; | ||
|  |   const before = _1(); | ||
|  |   const after = _2(); | ||
|  |   assert(before === '' && after === 'SubmitInput'); | ||
|  | })(); | ||
|  | ``` | ||
|  | 
 | ||
|  | The `h1` header should render the value of the `submit` field from the component's state. | ||
|  | 
 | ||
|  | ```js | ||
|  | (() => { | ||
|  |   const mockedComponent = Enzyme.mount(React.createElement(MyForm)); | ||
|  |   const _1 = () => { | ||
|  |     mockedComponent.setState({ input: '' }); | ||
|  |     mockedComponent.setState({ submit: '' }); | ||
|  |     mockedComponent | ||
|  |       .find('input') | ||
|  |       .simulate('change', { target: { value: 'TestInput' } }); | ||
|  |     return mockedComponent.find('h1').text(); | ||
|  |   }; | ||
|  |   const _2 = () => { | ||
|  |     mockedComponent.find('form').simulate('submit'); | ||
|  |     return mockedComponent.find('h1').text(); | ||
|  |   }; | ||
|  |   const before = _1(); | ||
|  |   const after = _2(); | ||
|  |   assert(before === '' && after === 'TestInput'); | ||
|  | })(); | ||
|  | ``` | ||
|  | 
 | ||
|  | # --seed--
 | ||
|  | 
 | ||
|  | ## --after-user-code--
 | ||
|  | 
 | ||
|  | ```jsx | ||
|  | ReactDOM.render(<MyForm />, document.getElementById('root')); | ||
|  | ``` | ||
|  | 
 | ||
|  | ## --seed-contents--
 | ||
|  | 
 | ||
|  | ```jsx | ||
|  | class MyForm extends React.Component { | ||
|  |   constructor(props) { | ||
|  |     super(props); | ||
|  |     this.state = { | ||
|  |       input: '', | ||
|  |       submit: '' | ||
|  |     }; | ||
|  |     this.handleChange = this.handleChange.bind(this); | ||
|  |     this.handleSubmit = this.handleSubmit.bind(this); | ||
|  |   } | ||
|  |   handleChange(event) { | ||
|  |     this.setState({ | ||
|  |       input: event.target.value | ||
|  |     }); | ||
|  |   } | ||
|  |   handleSubmit(event) { | ||
|  |     // Change code below this line | ||
|  | 
 | ||
|  |     // Change code above this line | ||
|  |   } | ||
|  |   render() { | ||
|  |     return ( | ||
|  |       <div> | ||
|  |         <form onSubmit={this.handleSubmit}> | ||
|  |           {/* Change code below this line */} | ||
|  | 
 | ||
|  |           {/* Change code above this line */} | ||
|  |           <button type='submit'>Submit!</button> | ||
|  |         </form> | ||
|  |         {/* Change code below this line */} | ||
|  | 
 | ||
|  |         {/* Change code above this line */} | ||
|  |       </div> | ||
|  |     ); | ||
|  |   } | ||
|  | } | ||
|  | ``` | ||
|  | 
 | ||
|  | # --solutions--
 | ||
|  | 
 | ||
|  | ```jsx | ||
|  | class MyForm extends React.Component { | ||
|  |   constructor(props) { | ||
|  |     super(props); | ||
|  |     this.state = { | ||
|  |       input: '', | ||
|  |       submit: '' | ||
|  |     }; | ||
|  |     this.handleChange = this.handleChange.bind(this); | ||
|  |     this.handleSubmit = this.handleSubmit.bind(this); | ||
|  |   } | ||
|  |   handleChange(event) { | ||
|  |     this.setState({ | ||
|  |       input: event.target.value | ||
|  |     }); | ||
|  |   } | ||
|  |   handleSubmit(event) { | ||
|  |     event.preventDefault(); | ||
|  |     this.setState(state => ({ | ||
|  |       submit: state.input | ||
|  |     })); | ||
|  |   } | ||
|  |   render() { | ||
|  |     return ( | ||
|  |       <div> | ||
|  |         <form onSubmit={this.handleSubmit}> | ||
|  |           <input value={this.state.input} onChange={this.handleChange} /> | ||
|  |           <button type='submit'>Submit!</button> | ||
|  |         </form> | ||
|  |         <h1>{this.state.submit}</h1> | ||
|  |       </div> | ||
|  |     ); | ||
|  |   } | ||
|  | } | ||
|  | ``` |