Tweak Forms
Dan Abramov committed
Oct 27, 2016 at 02:02 UTC
cc3dc21be01bd8f1e4d74d16a0cd795357c0be89
2 files changed
+148
-71
docs/docs/forms.md
+147
-70
@@ -9,7 +9,7 @@ redirect_from: "tips/controlled-input-null-value.html"
9
10
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.
11
12
-Two types of form components:
12
+There are two types of form components:
13
14
* Controlled Components
15
* Uncontrolled Components
@@ -20,15 +20,19 @@ You can jump directly to <a href="/react/docs/forms.html#examples">examples</a>.
20
21
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.
22
23
-```javascript
23
+```javascript{5}
24
render() {
25
- return <input type="text" value="Hello!" />;
25
+ return (
26
+ <input
27
+ type="text"
28
+ value="Hello!" />
29
+ );
30
}
31
```
32
29
-User input has no effect on the rendered element because React has declared the value to be "Hello!".
33
+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!"`.
34
31
-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 form:
35
+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:
36
37
```javascript{10,22,23}
38
class Form extends React.Component {
@@ -54,34 +58,53 @@ class Form extends React.Component {
58
placeholder="Hello!"
59
value={this.state.value}
60
onChange={this.handleChange} />
57
- <button onClick={this.handleSubmit}>Submit</button>
61
+ <button onClick={this.handleSubmit}>
62
+ Submit
63
+ </button>
64
</div>
65
);
66
}
67
}
68
63
-ReactDOM.render(<Form />, document.getElementById('root'));
69
+ReactDOM.render(
70
+ <Form />,
71
+ document.getElementById('root')
72
+);
73
```
74
66
-[Try it on CodePen.](https://codepen.io/ericnakagawa/pen/QKkJva?editors=0010)
75
+[Try it on CodePen.](https://codepen.io/gaearon/pen/NRmBmq?editors=0010)
76
77
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:
78
70
-```javascript
79
+```javascript{3}
80
handleChange(event) {
72
- this.setState({value: event.target.value.substr(0, 140)});
81
+ this.setState({
82
+ value: event.target.value.substr(0, 140)
83
+ });
84
}
85
```
86
87
This would accept user input and truncate the value to the first 140 characters.
88
89
+Controlled components also let us reset inputs to arbitrary values by setting the state:
90
+
91
+```javascript{3}
92
+ handleClearClick() {
93
+ this.setState({
94
+ value: ''
95
+ });
96
+ }
97
+```
98
+
99
### Potential Issues With Checkboxes and Radio Buttons
100
80
-Be aware that, in an attempt to normalize change handling for checkbox and radio inputs, React uses a `click` event in place of a `change` event. 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`.
101
+Be aware that, in an attempt to normalize change handling for checkbox and radio inputs, React listens to a `click` browser event to implement the `onChange` event.
102
+
103
+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`.
104
105
## Uncontrolled Components
106
84
-Form components that do not provide a `value` prop are *uncontrolled*.
107
+Form components that do not provide a `value` prop are **uncontrolled**.
108
109
The example below renders an `<input>` control with an empty value. Any user input will be immediately reflected by the rendered element.
110
@@ -95,7 +118,7 @@ An **uncontrolled** component manages its own state.
118
119
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.
120
98
-```javascript{10,22}
121
+```javascript{25}
122
class Form extends React.Component {
123
constructor(props) {
124
super(props);
@@ -105,6 +128,8 @@ class Form extends React.Component {
128
}
129
130
handleChange(event) {
131
+ // Note: with uncontrolled inputs, you don't
132
+ // have to put the value in the state.
133
this.setState({value: event.target.value});
134
}
135
@@ -115,21 +140,29 @@ class Form extends React.Component {
140
render() {
141
return (
142
<div>
118
- <input type="text"
143
+ <input
144
+ type="text"
145
placeholder="Hello!"
146
onChange={this.handleChange} />
121
- <button onClick={this.handleSubmit}>Submit</button>
147
+ <button onClick={this.handleSubmit}>
148
+ Submit
149
+ </button>
150
</div>
151
);
152
}
153
}
154
127
-ReactDOM.render(<Form />, document.getElementById('root'));
155
+ReactDOM.render(
156
+ <Form />,
157
+ document.getElementById('root')
158
+);
159
```
160
130
-[Try it on CodePen.](https://codepen.io/ericnakagawa/pen/XjxyoL?editors=0010)
161
+[Try it on CodePen.](https://codepen.io/gaearon/pen/pEBOJR?editors=0010)
162
+
163
+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.
164
132
-###Default Values
165
+### Default Values
166
167
To initialize an uncontrolled component with a non-empty value, you can supply a `defaultValue` prop.
168
@@ -190,7 +223,7 @@ Since this method describes the view at any point in time, the value of the text
223
In HTML, the value of `<textarea>` is usually set using its children:
224
225
```html
193
- <!-- antipattern: DO NOT DO THIS! -->
226
+ <!-- Don't do this in React. -->
227
<textarea name="description">This is the description.</textarea>
228
```
229
@@ -220,16 +253,16 @@ To make an uncontrolled component, `defaultValue` is used instead.
253
>
254
> 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']}>`.
255
223
-### Imperative operations
256
+### Imperative Operations
257
258
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).
259
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()`.
260
261
## Examples
262
230
-### Basic Controlled Input
263
+### Controlled Input
264
232
-```javascript
265
+```javascript{10,23,24}
266
class Form extends React.Component {
267
constructor(props) {
268
super(props);
@@ -237,6 +270,7 @@ class Form extends React.Component {
270
this.handleChange = this.handleChange.bind(this);
271
this.handleSubmit = this.handleSubmit.bind(this);
272
}
273
+
274
handleChange(event) {
275
this.setState({value: event.target.value});
276
}
@@ -254,21 +288,25 @@ class Form extends React.Component {
288
value={this.state.value}
289
onChange={this.handleChange}
290
/>
257
- <button onClick={this.handleSubmit}>Submit</button>
291
+ <button onClick={this.handleSubmit}>
292
+ Submit
293
+ </button>
294
</div>
295
);
296
}
297
}
298
263
-ReactDOM.render(<Form />, document.getElementById('root'));
299
+ReactDOM.render(
300
+ <Form />,
301
+ document.getElementById('root')
302
+);
303
```
304
266
-[Try this on CodePen.](https://codepen.io/ericnakagawa/pen/JRmZjz?editors=0010)
267
-
305
+[Try it out on CodePen.](https://codepen.io/gaearon/pen/JRVaYB?editors=0010)
306
269
-### Basic Controlled Textarea
307
+### Controlled Textarea
308
271
-```javascript
309
+```javascript{10,22,23}
310
class Form extends React.Component {
311
constructor(props) {
312
super(props);
@@ -293,20 +331,26 @@ class Form extends React.Component {
331
value={this.state.value}
332
onChange={this.handleChange}
333
/>
296
- <button onClick={this.handleSubmit}>Submit</button>
334
+ <br />
335
+ <button onClick={this.handleSubmit}>
336
+ Submit
337
+ </button>
338
</div>
339
);
340
}
341
}
342
302
-ReactDOM.render(<Form />, document.getElementById('root'));
343
+ReactDOM.render(
344
+ <Form />,
345
+ document.getElementById('root')
346
+);
347
```
348
305
-[Try this on CodePen.](https://codepen.io/ericnakagawa/pen/kkAQPp?editors=0010)
349
+[Try it out on CodePen.](https://codepen.io/gaearon/pen/NRmLxN?editors=0010)
350
307
-### Basic Uncontrolled Select
351
+### Controlled Select
352
309
-```javascript
353
+```javascript{10,20}
354
class Form extends React.Component {
355
constructor(props) {
356
super(props);
@@ -331,20 +375,25 @@ class Form extends React.Component {
375
<option value="B">Banana</option>
376
<option value="C">Cranberry</option>
377
</select>
334
- <button onClick={this.handleSubmit}>Submit</button>
378
+ <button onClick={this.handleSubmit}>
379
+ Submit
380
+ </button>
381
</div>
382
);
383
}
384
}
385
340
-ReactDOM.render(<Form />, document.getElementById('root'));
386
+ReactDOM.render(
387
+ <Form />,
388
+ document.getElementById('root')
389
+);
390
```
391
343
-[Try this on CodePen.](https://codepen.io/ericnakagawa/pen/pExQbL?editors=0010)
392
+[Try it out on CodePen.](https://codepen.io/gaearon/pen/qawrbr?editors=0010)
393
345
-### Basic Radio Button
394
+### Uncontrolled Radio Button
395
347
-```javascript
396
+```javascript{25,34,35,44}
397
class Form extends React.Component {
398
constructor(props) {
399
super(props);
@@ -365,38 +414,53 @@ class Form extends React.Component {
414
return (
415
<div>
416
<label>
368
- <input type="radio" value="A" id="choice-a"
417
+ <input
418
+ type="radio"
419
+ name="choice"
420
+ value="A"
421
onChange={this.handleChange} />
422
Option A
423
</label>
424
<br />
425
<label>
374
- <input type="radio" value="B" id="choice-b"
375
- onChange={this.handleChange} defaultChecked={true} />
426
+ <input
427
+ type="radio"
428
+ name="choice"
429
+ value="B"
430
+ onChange={this.handleChange}
431
+ defaultChecked={true} />
432
Option B
433
</label>
434
<br />
435
<label>
380
- <input type="radio" value="C" id="choice-c"
381
- onChange={this.handleChange} />
436
+ <input
437
+ type="radio"
438
+ name="choice"
439
+ value="C"
440
+ onChange={this.handleChange} />
441
Option C
442
</label>
384
- <br /><br />
385
- <button onClick={this.handleSubmit}>Submit</button>
443
+ <br />
444
+ <br />
445
+ <button onClick={this.handleSubmit}>
446
+ Submit
447
+ </button>
448
</div>
449
);
450
}
451
}
452
391
-ReactDOM.render(<Form />, document.getElementById('root'));
453
+ReactDOM.render(
454
+ <Form />,
455
+ document.getElementById('root')
456
+);
457
```
458
394
-[Try this on CodePen.](https://codepen.io/ericnakagawa/pen/WGaYVg?editors=0010)
459
+[Try it out on CodePen.](https://codepen.io/gaearon/pen/ozOPLJ?editors=0010)
460
461
+### Uncontrolled Checkbox
462
397
-### Basic Uncontrolled Checkbox
398
-
399
-```javascript
463
+```javascript{37,45,46,54}
464
class Form extends React.Component {
465
constructor(props) {
466
super(props);
@@ -406,10 +470,16 @@ class Form extends React.Component {
470
}
471
472
handleChange(event) {
409
- let value = event.target.value;
410
- let checked = this.state.checked; // copy
411
- if (!checked[value]) { checked[value] = true; } else { checked[value] = false; }
412
- this.setState({checked: checked})
473
+ const value = event.target.value;
474
+ // Copy the object so we don't mutate the old state.
475
+ // (This requires an Object.assign polyfill):
476
+ const checked = Object.assign({}, this.state.checked)
477
+ if (!checked[value]) {
478
+ checked[value] = true;
479
+ } else {
480
+ checked[value] = false;
481
+ };
482
+ this.setState({checked});
483
}
484
485
handleSubmit(event) {
@@ -424,36 +494,43 @@ class Form extends React.Component {
494
return (
495
<div>
496
<label>
427
- <input type="checkbox" value="A"
497
+ <input
498
+ type="checkbox"
499
+ value="A"
500
onChange={this.handleChange} />
501
Option A
502
</label>
503
<br />
504
<label>
433
- <input type="checkbox" value="B"
434
- onChange={this.handleChange} defaultChecked={true} />
505
+ <input
506
+ type="checkbox"
507
+ value="B"
508
+ onChange={this.handleChange}
509
+ defaultChecked={true} />
510
Option B
511
</label>
512
<br />
438
- <label><input type="checkbox" value="C"
513
+ <label>
514
+ <input
515
+ type="checkbox"
516
+ value="C"
517
onChange={this.handleChange} />
518
Option C
519
</label>
442
- <br /><br />
443
- <button onClick={this.handleSubmit}>Submit</button>
520
+ <br />
521
+ <br />
522
+ <button onClick={this.handleSubmit}>
523
+ Submit
524
+ </button>
525
</div>
526
);
527
}
528
}
448
-ReactDOM.render(<Form />, document.getElementById('root'));
449
-```
450
-
451
-[Try it on CodePen.](https://codepen.io/ericnakagawa/pen/kkAzPO?editors=0010)
452
-
453
-### Form Events
454
-
455
-Event names:
529
530
+ReactDOM.render(
531
+ <Form />,
532
+ document.getElementById('root')
533
+);
534
```
458
-onChange onInput onSubmit
459
-```
535
+
536
+[Try it on CodePen.](https://codepen.io/gaearon/pen/rrbkWz?editors=0010)
docs/docs/reference-events.md
+1
-1
@@ -35,7 +35,7 @@ string type
35
>
36
> As of v0.14, returning `false` from an event handler will no longer stop event propagation. Instead, `e.stopPropagation()` or `e.preventDefault()` should be triggered manually, as appropriate.
37
38
-### Event pooling
38
+### Event Pooling
39
40
The `SyntheticEvent` is pooled. This means that the `SyntheticEvent` object will be reused and all properties will be nullified after the event callback has been invoked.
41
This is for performance reasons.