@samitouri / QOS-React / commits / 3a09f4bf30

forms breakup

Kevin Lacker committed Nov 3, 2016 at 17:14 UTC 3a09f4bf30a8feaccfe5716d74de8b78f3d07a6a
3 files changed +138 -436
docs/_data/nav_docs.yml
+2 -1
@@ -34,6 +34,8 @@
34 title: Typechecking With PropTypes
35 - id: refs-and-the-dom
36 title: Refs and the DOM
37 + - id: uncontrolled-components
38 + title: Uncontrolled Components
39 - id: optimizing-performance
40 title: Optimizing Performance
41 - id: react-without-es6
@@ -80,4 +82,3 @@
82 title: Shallow Compare
83 - id: two-way-binding-helpers
84 title: Two-way Binding Helpers
83 -
docs/docs/forms.md
+76 -435
@@ -9,266 +9,89 @@ redirect_from:
9 - "docs/forms-zh-CN.html"
10 ---
11
12 -Form components such as `<input>`, `<textarea>`, and `<option>` differ from other native components because they can be mutated via user interactions. These components provide interfaces that make it easier to manage forms in response to user interactions.
12 +HTML form elements work a little bit differently from other DOM elements in React, because form elements naturally keep some internal state. For example, this form in plain HTML accepts a single name:
13
14 -There are two types of form components:
15 -
16 -* Controlled Components
17 -* Uncontrolled Components
14 +```html
15 +<form>
16 + Name:
17 + <input type="text" name="name" />
18 + <input type="submit" value="Submit" />
19 +</form>
20 +```
21
19 -You can jump directly to <a href="/react/docs/forms.html#examples">examples</a>.
22 +This form has the default HTML form behavior of browsing to a new page when the user submits the form. If you want this behavior in React, it just works. But in most cases, it's convenient to have a JavaScript function that handles the submission of the form and has access to the data that the user entered into the form. The standard way to achieve this is with a technique called "controlled components".
23
24 ## Controlled Components
25
23 -A **controlled** form component provides a `value` prop. A **controlled** component does not maintain its own internal state; the component renders purely based on props.
24 -
25 -```javascript{5}
26 -render() {
27 - return (
28 - <input
29 - type="text"
30 - value="Hello!" />
31 - );
32 -}
33 -```
26 +In HTML, form elements such as `<input>`, `<textarea>`, and `<select>` typically maintain their own state and update it based on user input. In React, mutable state is typically kept in the state property of components, and only updated with [`setState()`](/react/docs/react-component.html#setstate).
27
35 -If you try to run this example, you will notice that the input doesn't change as you type. This is because the component has declared the input's `value` to always be `"Hello!"`.
28 +We can combine the two by making the React state be the "single source of truth". Then the React component that renders a form also controls what happens in that form on subsequent user input. An input form element whose value is controlled by React in this way is called a "controlled component".
29
37 -To update the value in response to user input, you would use the `onChange` event to save the new value, then pass that to the `value` prop of the input:
30 +For example, if we want to make the previous example log the name when it is submitted, we can write the form as a controlled component:
31
39 -```javascript{10,22,23}
40 -class Form extends React.Component {
32 +```javascript{4,10-12,23}
33 +class NameForm extends React.Component {
34 constructor(props) {
35 super(props);
36 this.state = {value: ''};
44 - this.handleChange = this.handleChange.bind(this);
45 - this.handleSubmit = this.handleSubmit.bind(this);
46 - }
47 -
48 - handleChange(event) {
49 - this.setState({value: event.target.value});
50 - }
51 -
52 - handleSubmit(event) {
53 - alert('Text field value is: ' + this.state.value);
54 - }
55 -
56 - render() {
57 - return (
58 - <div>
59 - <input type="text"
60 - placeholder="Hello!"
61 - value={this.state.value}
62 - onChange={this.handleChange} />
63 - <button onClick={this.handleSubmit}>
64 - Submit
65 - </button>
66 - </div>
67 - );
68 - }
69 -}
70 -
71 -ReactDOM.render(
72 - <Form />,
73 - document.getElementById('root')
74 -);
75 -```
76 -
77 -[Try it on CodePen.](https://codepen.io/gaearon/pen/NRmBmq?editors=0010)
78 -
79 -In this example, we are accepting the value provided by the user and updating the `value` prop of the `<input>` component. This pattern makes it easy to implement interfaces that respond to or validate user interactions. For example:
80 -
81 -```javascript{3}
82 - handleChange(event) {
83 - this.setState({
84 - value: event.target.value.substr(0, 140)
85 - });
86 - }
87 -```
88 -
89 -This would accept user input and truncate the value to the first 140 characters.
90 -
91 -Controlled components also let us reset inputs to arbitrary values by setting the state:
92 -
93 -```javascript{3}
94 - handleClearClick() {
95 - this.setState({
96 - value: ''
97 - });
98 - }
99 -```
100 -
101 -### Potential Issues With Checkboxes and Radio Buttons
102 -
103 -Be aware that, in an attempt to normalize change handling for checkboxes and radio inputs, React listens to a `click` browser event to implement the `onChange` event.
104 -
105 -For the most part this behaves as expected, except when calling `preventDefault` in a `change` handler. `preventDefault` stops the browser from visually updating the input, even if `checked` gets toggled. This can be worked around either by removing the call to `preventDefault`, or putting the toggle of `checked` in a `setTimeout`.
106 -
107 -## Uncontrolled Components
108 -
109 -Form components that do not provide a `value` prop are **uncontrolled**.
37
111 -The example below renders an `<input>` control with an empty value. Any user input will be immediately reflected by the rendered element.
112 -
113 -An **uncontrolled** component manages its own state.
114 -
115 -```javascript
116 - render() {
117 - return <input type="text" />;
118 - }
119 -```
120 -
121 -If you wanted to listen to updates to the value, you could use the `onChange` event just like you can with controlled components. However, you would _not_ pass the value you saved to the component.
122 -
123 -```javascript{25}
124 -class Form extends React.Component {
125 - constructor(props) {
126 - super(props);
127 - this.state = {value: ''};
38 this.handleChange = this.handleChange.bind(this);
39 this.handleSubmit = this.handleSubmit.bind(this);
40 }
41
42 handleChange(event) {
133 - // Note: with uncontrolled inputs, you don't
134 - // have to put the value in the state.
43 this.setState({value: event.target.value});
44 }
45
46 handleSubmit(event) {
139 - alert('Text field value is: ' + this.state.value);
47 + alert('A name was submitted: ' + this.state.value);
48 + event.preventDefault();
49 }
50
51 render() {
52 return (
144 - <div>
145 - <input
146 - type="text"
147 - placeholder="Hello!"
148 - onChange={this.handleChange} />
149 - <button onClick={this.handleSubmit}>
150 - Submit
151 - </button>
152 - </div>
53 + <form onSubmit={this.handleSubmit}>
54 + Name:
55 + <input type="text" value={this.state.value} onChange={this.handleChange} />
56 + <input type="submit" value="Submit" />
57 + </form>
58 );
59 }
60 }
156 -
157 -ReactDOM.render(
158 - <Form />,
159 - document.getElementById('root')
160 -);
161 -```
162 -
163 -[Try it on CodePen.](https://codepen.io/gaearon/pen/pEBOJR?editors=0010)
164 -
165 -While this example puts value in the state so we can later read it in `handleSubmit()`, uncontrolled form components don't require this. You may completely omit an `onChange` handler and instead read the input value using [DOM references](/react/docs/refs-and-the-dom.html), an advanced feature discussed later.
166 -
167 -### Default Values
168 -
169 -To initialize an uncontrolled component with a non-empty value, you can supply a `defaultValue` prop.
170 -
171 -```javascript
172 - render() {
173 - return <input type="text" defaultValue="Hello!" />;
174 - }
61 ```
62
177 -Likewise, `<input type="checkbox">` and `<input type="radio">` support `defaultChecked`, and `<select>` supports `defaultValue`.
178 -
179 -## Interactive Props
63 +[Try it on CodePen.](https://codepen.io/lacker/pen/oYNzxY?editors=0010)
64
181 -Form components support a few props that are affected via user interactions:
65 +Since the `value` attribute is set on our form element, the displayed value will always be `this.state.value`, making the React state the source of truth. Since `handleChange` runs on every keystroke to update the React state, the displayed value will update as the user types.
66
183 -* `value`, supported by `<input>` and `<textarea>` components.
184 -* `checked`, supported by `<input>` components of type `checkbox` or `radio`.
185 -* `selected`, supported by `<option>` components.
67 +With a controlled component, every state mutation will have an associated handler function. This makes it straightforward to modify or validate user input. For example, if we wanted to enforce that names are written with all uppercase letters, we could write `handleChange` as:
68
187 -In HTML, the value of `<textarea>` is set via children. In React, you should use `value` instead.
188 -
189 -Form components allow listening for changes by setting a callback to the `onChange` prop. The `onChange` prop works across browsers to fire in response to user interactions when:
190 -
191 -* The `value` of `<input>` or `<textarea>` changes.
192 -* The `checked` state of `<input>` changes.
193 -* The `selected` state of `<option>` changes.
194 -
195 -Like all DOM events, the `onChange` prop is supported on all native components and can be used to listen to bubbled change events.
196 -
197 -> Note:
198 ->
199 -> For `<input>` and `<textarea>`, `onChange` should generally be used instead of the DOM's built-in [`oninput`](https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/oninput) event handler.
200 -
201 -## Advanced Topics
202 -
203 -### Why Controlled Components?
204 -
205 -Using form components such as `<input>` in React presents a challenge that is absent when writing traditional form HTML. For example, in HTML:
206 -
207 -```html
208 - <input type="text" name="title" value="Untitled" />
209 -```
210 -
211 -This renders an input *initialized* with the value, `Untitled`. When the user updates the input, the node's `value` *property* will change. However, `node.getAttribute('value')` will still return the value used at initialization time, `Untitled`.
212 -
213 -Unlike HTML, React components must represent the state of the view at any point in time and not only at initialization time. For example, in React:
214 -
215 -```javascript
216 - render() {
217 - return <input type="text" name="title" value="Untitled" />;
218 - }
69 +```javascript{2}
70 +handleChange(event) {
71 + this.setState({value: event.target.value.toUpperCase()});
72 +}
73 ```
74
221 -Since this method describes the view at any point in time, the value of the text input should *always* be `Untitled`.
222 -
223 -### Why Textarea Value?
75 +## The textarea Tag
76
225 -In HTML, the value of `<textarea>` is usually set using its children:
77 +In HTML, a `<textarea>` element defines its text by its children:
78
79 ```html
228 - <!-- Don't do this in React. -->
229 - <textarea name="description">This is the description.</textarea>
230 -```
231 -
232 -For HTML, this easily allows developers to supply multiline values. However, since React is JavaScript, we do not have string limitations and can use `\n` if we want newlines. In a world where we have `value` and `defaultValue`, it is ambiguous what role children play. For this reason, you should not use children when setting `<textarea>` values:
233 -
234 -```javascript
235 - <textarea name="description" value="This is a description." />
80 +<textarea>
81 + Hello there, this is some text in a text area
82 +</textarea>
83 ```
84
238 -If you *do* decide to use children, they will behave like `defaultValue`.
85 +In React, a `<textarea>` uses a `value` attribute instead. This way, a form using a `<textarea>` can be written very similarly to a form that uses a single-line input:
86
240 -### Why Select Value?
241 -
242 -The selected `<option>` in an HTML `<select>` is normally specified through that option's `selected` attribute. In React we assign the `select` component a specific value by passing a `value` prop:
243 -
244 -```javascript{1}
245 - <select value="B">
246 - <option value="A">Apple</option>
247 - <option value="B">Banana</option>
248 - <option value="C">Cranberry</option>
249 - </select>
250 -```
251 -
252 -To make an uncontrolled component, `defaultValue` is used instead.
253 -
254 -> Note:
255 ->
256 -> You can pass an array into the `value` attribute, allowing you to select multiple options in a `select` tag: `<select multiple={true} value={['B', 'C']}>`.
257 -
258 -### Imperative Operations
259 -
260 -If you need to imperatively perform an operation, you have to obtain a [reference to the DOM node](/react/docs/more-about-refs.html#the-ref-callback-attribute).
261 -For instance, if you want to imperatively submit a form, one approach would be to attach a `ref` to the `form` element and manually call `form.submit()`.
262 -
263 -## Examples
264 -
265 -### Controlled Input
266 -
267 -```javascript{10,23,24}
268 -class Form extends React.Component {
87 +```javascript{4-6,12-14,25}
88 +class EssayForm extends React.Component {
89 constructor(props) {
90 super(props);
271 - this.state = {value: ''};
91 + this.state = {
92 + value: 'Please write an essay about your favorite DOM element.'
93 + };
94 +
95 this.handleChange = this.handleChange.bind(this);
96 this.handleSubmit = this.handleSubmit.bind(this);
97 }
@@ -278,85 +101,45 @@ class Form extends React.Component {
101 }
102
103 handleSubmit(event) {
281 - alert('Text field value is: ' + this.state.value);
104 + alert('An essay was submitted: ' + this.state.value);
105 + event.preventDefault();
106 }
107
108 render() {
109 return (
286 - <div>
287 - <input
288 - type="text"
289 - placeholder="edit me"
290 - value={this.state.value}
291 - onChange={this.handleChange}
292 - />
293 - <button onClick={this.handleSubmit}>
294 - Submit
295 - </button>
296 - </div>
110 + <form onSubmit={this.handleSubmit}>
111 + Name:
112 + <textarea value={this.state.value} onChange={this.handleChange} />
113 + <input type="submit" value="Submit" />
114 + </form>
115 );
116 }
117 }
300 -
301 -ReactDOM.render(
302 - <Form />,
303 - document.getElementById('root')
304 -);
118 ```
119
307 -[Try it on CodePen.](https://codepen.io/gaearon/pen/JRVaYB?editors=0010)
308 -
309 -### Controlled Textarea
310 -
311 -```javascript{10,22,23}
312 -class Form extends React.Component {
313 - constructor(props) {
314 - super(props);
315 - this.state = {value: ''};
316 - this.handleChange = this.handleChange.bind(this);
317 - this.handleSubmit = this.handleSubmit.bind(this);
318 - }
319 -
320 - handleChange(event) {
321 - this.setState({value: event.target.value});
322 - }
120 +Notice that `this.state.value` is initialized in the constructor, so that the text area starts off with some text in it.
121
324 - handleSubmit(event) {
325 - alert('Textarea value is: ' + this.state.value);
326 - }
122 +## The select Tag
123
328 - render() {
329 - return (
330 - <div>
331 - <textarea
332 - name="description"
333 - value={this.state.value}
334 - onChange={this.handleChange}
335 - />
336 - <br />
337 - <button onClick={this.handleSubmit}>
338 - Submit
339 - </button>
340 - </div>
341 - );
342 - }
343 -}
124 +In HTML, `<select>` creates a drop-down list. For example, this HTML creates a drop-down list of flavors:
125
345 -ReactDOM.render(
346 - <Form />,
347 - document.getElementById('root')
348 -);
126 +```html
127 +<select>
128 + <option value="grapefruit">Grapefruit</option>
129 + <option value="lime">Lime</option>
130 + <option selected value="coconut">Coconut</option>
131 + <option value="mango">Mango</option>
132 +</select>
133 ```
134
351 -[Try it on CodePen.](https://codepen.io/gaearon/pen/NRmLxN?editors=0010)
352 -
353 -### Controlled Select
135 +Note that the Coconut option is initially selected, because of the `selected` attribute. React, instead of using this `selected` attribute, uses a `value` attribute on the root `select` tag. This is more convenient in a controlled component because you only need to update it in one place. For example:
136
355 -```javascript{10,20}
356 -class Form extends React.Component {
137 +```javascript{4,10-12,23}
138 +class FlavorForm extends React.Component {
139 constructor(props) {
140 super(props);
359 - this.state = {value: 'B'};
141 + this.state = {value: 'coconut'};
142 +
143 this.handleChange = this.handleChange.bind(this);
144 this.handleSubmit = this.handleSubmit.bind(this);
145 }
@@ -366,173 +149,31 @@ class Form extends React.Component {
149 }
150
151 handleSubmit(event) {
369 - alert('Select value is: ' + this.state.value);
152 + alert('Your favorite flavor is: ' + this.state.value);
153 + event.preventDefault();
154 }
155
156 render() {
157 return (
374 - <div>
158 + <form onSubmit={this.handleSubmit}>
159 + Pick your favorite La Croix flavor:
160 <select value={this.state.value} onChange={this.handleChange}>
376 - <option value="A">Apple</option>
377 - <option value="B">Banana</option>
378 - <option value="C">Cranberry</option>
161 + <option value="grapefruit">Grapefruit</option>
162 + <option value="lime">Lime</option>
163 + <option value="coconut">Coconut</option>
164 + <option value="mango">Mango</option>
165 </select>
380 - <button onClick={this.handleSubmit}>
381 - Submit
382 - </button>
383 - </div>
384 - );
385 - }
386 -}
387 -
388 -ReactDOM.render(
389 - <Form />,
390 - document.getElementById('root')
391 -);
392 -```
393 -
394 -[Try it on CodePen.](https://codepen.io/gaearon/pen/qawrbr?editors=0010)
395 -
396 -### Uncontrolled Radio Button
397 -
398 -```javascript{25,34,35,44}
399 -class Form extends React.Component {
400 - constructor(props) {
401 - super(props);
402 - this.value = 'B';
403 - this.handleChange = this.handleChange.bind(this);
404 - this.handleSubmit = this.handleSubmit.bind(this);
405 - }
406 -
407 - handleChange(event) {
408 - this.value = event.target.value;
409 - }
410 -
411 - handleSubmit(event) {
412 - alert('Radio button value is: ' + this.value);
413 - }
414 -
415 - render() {
416 - return (
417 - <div>
418 - <label>
419 - <input
420 - type="radio"
421 - name="choice"
422 - value="A"
423 - onChange={this.handleChange} />
424 - Option A
425 - </label>
426 - <br />
427 - <label>
428 - <input
429 - type="radio"
430 - name="choice"
431 - value="B"
432 - onChange={this.handleChange}
433 - defaultChecked={true} />
434 - Option B
435 - </label>
436 - <br />
437 - <label>
438 - <input
439 - type="radio"
440 - name="choice"
441 - value="C"
442 - onChange={this.handleChange} />
443 - Option C
444 - </label>
445 - <br />
446 - <br />
447 - <button onClick={this.handleSubmit}>
448 - Submit
449 - </button>
450 - </div>
166 + <input type="submit" value="Submit" />
167 + </form>
168 );
169 }
170 }
454 -
455 -ReactDOM.render(
456 - <Form />,
457 - document.getElementById('root')
458 -);
171 ```
172
461 -[Try it on CodePen.](https://codepen.io/gaearon/pen/ozOPLJ?editors=0010)
462 -
463 -### Uncontrolled Checkbox
173 +[Try it on CodePen.](https://codepen.io/lacker/pen/QGWKQP?editors=0010)
174
465 -```javascript{37,45,46,54}
466 -class Form extends React.Component {
467 - constructor(props) {
468 - super(props);
469 - this.state = {checked: {'A': false, 'B': true, 'C': false}};
470 - this.handleChange = this.handleChange.bind(this);
471 - this.handleSubmit = this.handleSubmit.bind(this);
472 - }
473 -
474 - handleChange(event) {
475 - const value = event.target.value;
476 - // Copy the object so we don't mutate the old state.
477 - // (This requires an Object.assign polyfill):
478 - const checked = Object.assign({}, this.state.checked)
479 - if (!checked[value]) {
480 - checked[value] = true;
481 - } else {
482 - checked[value] = false;
483 - };
484 - this.setState({checked});
485 - }
486 -
487 - handleSubmit(event) {
488 - alert('Boxes checked: ' +
489 - (this.state.checked.A ? 'A ' : '') +
490 - (this.state.checked.B ? 'B ' : '') +
491 - (this.state.checked.C ? 'C' : '')
492 - );
493 - }
494 -
495 - render() {
496 - return (
497 - <div>
498 - <label>
499 - <input
500 - type="checkbox"
501 - value="A"
502 - onChange={this.handleChange} />
503 - Option A
504 - </label>
505 - <br />
506 - <label>
507 - <input
508 - type="checkbox"
509 - value="B"
510 - onChange={this.handleChange}
511 - defaultChecked={true} />
512 - Option B
513 - </label>
514 - <br />
515 - <label>
516 - <input
517 - type="checkbox"
518 - value="C"
519 - onChange={this.handleChange} />
520 - Option C
521 - </label>
522 - <br />
523 - <br />
524 - <button onClick={this.handleSubmit}>
525 - Submit
526 - </button>
527 - </div>
528 - );
529 - }
530 -}
175 +Overall, this makes it so that `<input type="text">`, `<textarea>`, and `<select>` all work very similarly - they all accept a `value` attribute that you can use to implement a controlled component.
176
532 -ReactDOM.render(
533 - <Form />,
534 - document.getElementById('root')
535 -);
536 -```
177 +## Alternatives to Controlled Components
178
538 -[Try it on CodePen.](https://codepen.io/gaearon/pen/rrbkWz?editors=0010)
179 +It can sometimes be tedious to use controlled components, because you need to write an event handler for every way your data can change and pipe all of the input state through a React component. This can become particularly annoying when you are converting a preexisting codebase to React, or integrating a React application with a non-React library. In these situations, you might want to check out [uncontrolled components](/react/docs/uncontrolled-components.html), an alternative technique for implementing input forms.
docs/docs/uncontrolled-components.md new
+60
@@ -0,0 +1,60 @@
1 +---
2 +id: uncontrolled-components
3 +title: Uncontrolled Components
4 +permalink: docs/uncontrolled-components.html
5 +---
6 +
7 +In most cases, we recommend using [controlled components](/react/docs/forms.html) to implement forms. In a controlled component, form data is handled by a React component. The alternative is uncontrolled components, where form data is handled by the DOM itself.
8 +
9 +To write an uncontrolled component, instead of writing an event handler for every state update, you can [use a ref](/react/docs/refs-and-the-dom.html) to get form values from the DOM.
10 +
11 +For example, this code accepts a single name in an uncontrolled component:
12 +
13 +```javascript{8,16}
14 +class NameForm extends React.Component {
15 + constructor(props) {
16 + super(props);
17 + this.handleSubmit = this.handleSubmit.bind(this);
18 + }
19 +
20 + handleSubmit(event) {
21 + alert('A name was submitted: ' + this.input.value);
22 + event.preventDefault();
23 + }
24 +
25 + render() {
26 + return (
27 + <form onSubmit={this.handleSubmit}>
28 + Name:
29 + <input type="text" ref={(input) => this.input = input} />
30 + <input type="submit" value="Submit" />
31 + </form>
32 + );
33 + }
34 +}
35 +```
36 +
37 +[Try it on CodePen.](https://codepen.io/lacker/pen/XNWjOj?editors=0010)
38 +
39 +Since an uncontrolled component keeps the source of truth in the DOM, it is sometimes easier to integrate React and non-React code when using uncontrolled components. It can also be slightly less code if you want to be quick and dirty. Otherwise, you should usually use controlled components.
40 +
41 +### Default Values
42 +
43 +In the React rendering lifecycle, the `value` attribute on form elements will override the value in the DOM. With an uncontrolled component, you often want React to specify the initial value, but leave subsequent updates uncontrolled. To handle this case, you can specify a `defaultValue` attribute instead of `value`.
44 +
45 +```javascript{6}
46 +render() {
47 + return (
48 + <form onSubmit={this.handleSubmit}>
49 + Name:
50 + <input
51 + defaultValue="Bob"
52 + type="text"
53 + ref={(input) => this.input = input} />
54 + <input type="submit" value="Submit" />
55 + </form>
56 + );
57 +}
58 +```
59 +
60 +Likewise, `<input type="checkbox">` and `<input type="radio">` support `defaultChecked`, and `<select>` supports `defaultValue`.