mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Tweak Forms
This commit is contained in:
+147
-70
@@ -9,7 +9,7 @@ redirect_from: "tips/controlled-input-null-value.html"
|
||||
|
||||
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.
|
||||
|
||||
Two types of form components:
|
||||
There are two types of form components:
|
||||
|
||||
* Controlled Components
|
||||
* Uncontrolled Components
|
||||
@@ -20,15 +20,19 @@ You can jump directly to <a href="/react/docs/forms.html#examples">examples</a>.
|
||||
|
||||
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.
|
||||
|
||||
```javascript
|
||||
```javascript{5}
|
||||
render() {
|
||||
return <input type="text" value="Hello!" />;
|
||||
return (
|
||||
<input
|
||||
type="text"
|
||||
value="Hello!" />
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
User input has no effect on the rendered element because React has declared the value to be "Hello!".
|
||||
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!"`.
|
||||
|
||||
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:
|
||||
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:
|
||||
|
||||
```javascript{10,22,23}
|
||||
class Form extends React.Component {
|
||||
@@ -54,34 +58,53 @@ class Form extends React.Component {
|
||||
placeholder="Hello!"
|
||||
value={this.state.value}
|
||||
onChange={this.handleChange} />
|
||||
<button onClick={this.handleSubmit}>Submit</button>
|
||||
<button onClick={this.handleSubmit}>
|
||||
Submit
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ReactDOM.render(<Form />, document.getElementById('root'));
|
||||
ReactDOM.render(
|
||||
<Form />,
|
||||
document.getElementById('root')
|
||||
);
|
||||
```
|
||||
|
||||
[Try it on CodePen.](https://codepen.io/ericnakagawa/pen/QKkJva?editors=0010)
|
||||
[Try it on CodePen.](https://codepen.io/gaearon/pen/NRmBmq?editors=0010)
|
||||
|
||||
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:
|
||||
|
||||
```javascript
|
||||
```javascript{3}
|
||||
handleChange(event) {
|
||||
this.setState({value: event.target.value.substr(0, 140)});
|
||||
this.setState({
|
||||
value: event.target.value.substr(0, 140)
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
This would accept user input and truncate the value to the first 140 characters.
|
||||
|
||||
Controlled components also let us reset inputs to arbitrary values by setting the state:
|
||||
|
||||
```javascript{3}
|
||||
handleClearClick() {
|
||||
this.setState({
|
||||
value: ''
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### Potential Issues With Checkboxes and Radio Buttons
|
||||
|
||||
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`.
|
||||
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.
|
||||
|
||||
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`.
|
||||
|
||||
## Uncontrolled Components
|
||||
|
||||
Form components that do not provide a `value` prop are *uncontrolled*.
|
||||
Form components that do not provide a `value` prop are **uncontrolled**.
|
||||
|
||||
The example below renders an `<input>` control with an empty value. Any user input will be immediately reflected by the rendered element.
|
||||
|
||||
@@ -95,7 +118,7 @@ An **uncontrolled** component manages its own state.
|
||||
|
||||
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.
|
||||
|
||||
```javascript{10,22}
|
||||
```javascript{25}
|
||||
class Form extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
@@ -105,6 +128,8 @@ class Form extends React.Component {
|
||||
}
|
||||
|
||||
handleChange(event) {
|
||||
// Note: with uncontrolled inputs, you don't
|
||||
// have to put the value in the state.
|
||||
this.setState({value: event.target.value});
|
||||
}
|
||||
|
||||
@@ -115,21 +140,29 @@ class Form extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<input type="text"
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Hello!"
|
||||
onChange={this.handleChange} />
|
||||
<button onClick={this.handleSubmit}>Submit</button>
|
||||
<button onClick={this.handleSubmit}>
|
||||
Submit
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ReactDOM.render(<Form />, document.getElementById('root'));
|
||||
ReactDOM.render(
|
||||
<Form />,
|
||||
document.getElementById('root')
|
||||
);
|
||||
```
|
||||
|
||||
[Try it on CodePen.](https://codepen.io/ericnakagawa/pen/XjxyoL?editors=0010)
|
||||
[Try it on CodePen.](https://codepen.io/gaearon/pen/pEBOJR?editors=0010)
|
||||
|
||||
###Default Values
|
||||
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.
|
||||
|
||||
### Default Values
|
||||
|
||||
To initialize an uncontrolled component with a non-empty value, you can supply a `defaultValue` prop.
|
||||
|
||||
@@ -190,7 +223,7 @@ Since this method describes the view at any point in time, the value of the text
|
||||
In HTML, the value of `<textarea>` is usually set using its children:
|
||||
|
||||
```html
|
||||
<!-- antipattern: DO NOT DO THIS! -->
|
||||
<!-- Don't do this in React. -->
|
||||
<textarea name="description">This is the description.</textarea>
|
||||
```
|
||||
|
||||
@@ -220,16 +253,16 @@ To make an uncontrolled component, `defaultValue` is used instead.
|
||||
>
|
||||
> 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']}>`.
|
||||
|
||||
### Imperative operations
|
||||
### Imperative Operations
|
||||
|
||||
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).
|
||||
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()`.
|
||||
|
||||
## Examples
|
||||
|
||||
### Basic Controlled Input
|
||||
### Controlled Input
|
||||
|
||||
```javascript
|
||||
```javascript{10,23,24}
|
||||
class Form extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
@@ -237,6 +270,7 @@ class Form extends React.Component {
|
||||
this.handleChange = this.handleChange.bind(this);
|
||||
this.handleSubmit = this.handleSubmit.bind(this);
|
||||
}
|
||||
|
||||
handleChange(event) {
|
||||
this.setState({value: event.target.value});
|
||||
}
|
||||
@@ -254,21 +288,25 @@ class Form extends React.Component {
|
||||
value={this.state.value}
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
<button onClick={this.handleSubmit}>Submit</button>
|
||||
<button onClick={this.handleSubmit}>
|
||||
Submit
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ReactDOM.render(<Form />, document.getElementById('root'));
|
||||
ReactDOM.render(
|
||||
<Form />,
|
||||
document.getElementById('root')
|
||||
);
|
||||
```
|
||||
|
||||
[Try this on CodePen.](https://codepen.io/ericnakagawa/pen/JRmZjz?editors=0010)
|
||||
[Try it out on CodePen.](https://codepen.io/gaearon/pen/JRVaYB?editors=0010)
|
||||
|
||||
### Controlled Textarea
|
||||
|
||||
### Basic Controlled Textarea
|
||||
|
||||
```javascript
|
||||
```javascript{10,22,23}
|
||||
class Form extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
@@ -293,20 +331,26 @@ class Form extends React.Component {
|
||||
value={this.state.value}
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
<button onClick={this.handleSubmit}>Submit</button>
|
||||
<br />
|
||||
<button onClick={this.handleSubmit}>
|
||||
Submit
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ReactDOM.render(<Form />, document.getElementById('root'));
|
||||
ReactDOM.render(
|
||||
<Form />,
|
||||
document.getElementById('root')
|
||||
);
|
||||
```
|
||||
|
||||
[Try this on CodePen.](https://codepen.io/ericnakagawa/pen/kkAQPp?editors=0010)
|
||||
[Try it out on CodePen.](https://codepen.io/gaearon/pen/NRmLxN?editors=0010)
|
||||
|
||||
### Basic Uncontrolled Select
|
||||
### Controlled Select
|
||||
|
||||
```javascript
|
||||
```javascript{10,20}
|
||||
class Form extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
@@ -331,20 +375,25 @@ class Form extends React.Component {
|
||||
<option value="B">Banana</option>
|
||||
<option value="C">Cranberry</option>
|
||||
</select>
|
||||
<button onClick={this.handleSubmit}>Submit</button>
|
||||
<button onClick={this.handleSubmit}>
|
||||
Submit
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ReactDOM.render(<Form />, document.getElementById('root'));
|
||||
ReactDOM.render(
|
||||
<Form />,
|
||||
document.getElementById('root')
|
||||
);
|
||||
```
|
||||
|
||||
[Try this on CodePen.](https://codepen.io/ericnakagawa/pen/pExQbL?editors=0010)
|
||||
[Try it out on CodePen.](https://codepen.io/gaearon/pen/qawrbr?editors=0010)
|
||||
|
||||
### Basic Radio Button
|
||||
### Uncontrolled Radio Button
|
||||
|
||||
```javascript
|
||||
```javascript{25,34,35,44}
|
||||
class Form extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
@@ -365,38 +414,53 @@ class Form extends React.Component {
|
||||
return (
|
||||
<div>
|
||||
<label>
|
||||
<input type="radio" value="A" id="choice-a"
|
||||
<input
|
||||
type="radio"
|
||||
name="choice"
|
||||
value="A"
|
||||
onChange={this.handleChange} />
|
||||
Option A
|
||||
</label>
|
||||
<br />
|
||||
<label>
|
||||
<input type="radio" value="B" id="choice-b"
|
||||
onChange={this.handleChange} defaultChecked={true} />
|
||||
<input
|
||||
type="radio"
|
||||
name="choice"
|
||||
value="B"
|
||||
onChange={this.handleChange}
|
||||
defaultChecked={true} />
|
||||
Option B
|
||||
</label>
|
||||
<br />
|
||||
<label>
|
||||
<input type="radio" value="C" id="choice-c"
|
||||
onChange={this.handleChange} />
|
||||
<input
|
||||
type="radio"
|
||||
name="choice"
|
||||
value="C"
|
||||
onChange={this.handleChange} />
|
||||
Option C
|
||||
</label>
|
||||
<br /><br />
|
||||
<button onClick={this.handleSubmit}>Submit</button>
|
||||
<br />
|
||||
<br />
|
||||
<button onClick={this.handleSubmit}>
|
||||
Submit
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ReactDOM.render(<Form />, document.getElementById('root'));
|
||||
ReactDOM.render(
|
||||
<Form />,
|
||||
document.getElementById('root')
|
||||
);
|
||||
```
|
||||
|
||||
[Try this on CodePen.](https://codepen.io/ericnakagawa/pen/WGaYVg?editors=0010)
|
||||
[Try it out on CodePen.](https://codepen.io/gaearon/pen/ozOPLJ?editors=0010)
|
||||
|
||||
### Uncontrolled Checkbox
|
||||
|
||||
### Basic Uncontrolled Checkbox
|
||||
|
||||
```javascript
|
||||
```javascript{37,45,46,54}
|
||||
class Form extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
@@ -406,10 +470,16 @@ class Form extends React.Component {
|
||||
}
|
||||
|
||||
handleChange(event) {
|
||||
let value = event.target.value;
|
||||
let checked = this.state.checked; // copy
|
||||
if (!checked[value]) { checked[value] = true; } else { checked[value] = false; }
|
||||
this.setState({checked: checked})
|
||||
const value = event.target.value;
|
||||
// Copy the object so we don't mutate the old state.
|
||||
// (This requires an Object.assign polyfill):
|
||||
const checked = Object.assign({}, this.state.checked)
|
||||
if (!checked[value]) {
|
||||
checked[value] = true;
|
||||
} else {
|
||||
checked[value] = false;
|
||||
};
|
||||
this.setState({checked});
|
||||
}
|
||||
|
||||
handleSubmit(event) {
|
||||
@@ -424,36 +494,43 @@ class Form extends React.Component {
|
||||
return (
|
||||
<div>
|
||||
<label>
|
||||
<input type="checkbox" value="A"
|
||||
<input
|
||||
type="checkbox"
|
||||
value="A"
|
||||
onChange={this.handleChange} />
|
||||
Option A
|
||||
</label>
|
||||
<br />
|
||||
<label>
|
||||
<input type="checkbox" value="B"
|
||||
onChange={this.handleChange} defaultChecked={true} />
|
||||
<input
|
||||
type="checkbox"
|
||||
value="B"
|
||||
onChange={this.handleChange}
|
||||
defaultChecked={true} />
|
||||
Option B
|
||||
</label>
|
||||
<br />
|
||||
<label><input type="checkbox" value="C"
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
value="C"
|
||||
onChange={this.handleChange} />
|
||||
Option C
|
||||
</label>
|
||||
<br /><br />
|
||||
<button onClick={this.handleSubmit}>Submit</button>
|
||||
<br />
|
||||
<br />
|
||||
<button onClick={this.handleSubmit}>
|
||||
Submit
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
ReactDOM.render(<Form />, document.getElementById('root'));
|
||||
|
||||
ReactDOM.render(
|
||||
<Form />,
|
||||
document.getElementById('root')
|
||||
);
|
||||
```
|
||||
|
||||
[Try it on CodePen.](https://codepen.io/ericnakagawa/pen/kkAzPO?editors=0010)
|
||||
|
||||
### Form Events
|
||||
|
||||
Event names:
|
||||
|
||||
```
|
||||
onChange onInput onSubmit
|
||||
```
|
||||
[Try it on CodePen.](https://codepen.io/gaearon/pen/rrbkWz?editors=0010)
|
||||
|
||||
@@ -35,7 +35,7 @@ string type
|
||||
>
|
||||
> 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.
|
||||
|
||||
### Event pooling
|
||||
### Event Pooling
|
||||
|
||||
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.
|
||||
This is for performance reasons.
|
||||
|
||||
Reference in New Issue
Block a user