Browse Source

forms breakup

main
Kevin Lacker 8 years ago
committed by Dan Abramov
parent
commit
809e28223e
  1. 3
      _data/nav_docs.yml
  2. 511
      docs/forms.md
  3. 60
      docs/uncontrolled-components.md

3
_data/nav_docs.yml

@ -34,6 +34,8 @@
title: Typechecking With PropTypes
- id: refs-and-the-dom
title: Refs and the DOM
- id: uncontrolled-components
title: Uncontrolled Components
- id: optimizing-performance
title: Optimizing Performance
- id: react-without-es6
@ -80,4 +82,3 @@
title: Shallow Compare
- id: two-way-binding-helpers
title: Two-way Binding Helpers

511
docs/forms.md

@ -9,266 +9,89 @@ redirect_from:
- "docs/forms-zh-CN.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.
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:
There are two types of form components:
* Controlled Components
* Uncontrolled Components
```html
<form>
Name:
<input type="text" name="name" />
<input type="submit" value="Submit" />
</form>
```
You can jump directly to <a href="/react/docs/forms.html#examples">examples</a>.
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".
## Controlled Components
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{5}
render() {
return (
<input
type="text"
value="Hello!" />
);
}
```
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).
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!"`.
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".
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:
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:
```javascript{10,22,23}
class Form extends React.Component {
```javascript{4,10-12,23}
class NameForm extends React.Component {
constructor(props) {
super(props);
this.state = {value: ''};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('Text field value is: ' + this.state.value);
}
render() {
return (
<div>
<input type="text"
placeholder="Hello!"
value={this.state.value}
onChange={this.handleChange} />
<button onClick={this.handleSubmit}>
Submit
</button>
</div>
);
}
}
ReactDOM.render(
<Form />,
document.getElementById('root')
);
```
[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{3}
handleChange(event) {
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 checkboxes 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**.
The example below renders an `<input>` control with an empty value. Any user input will be immediately reflected by the rendered element.
An **uncontrolled** component manages its own state.
```javascript
render() {
return <input type="text" />;
}
```
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{25}
class Form extends React.Component {
constructor(props) {
super(props);
this.state = {value: ''};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
// Note: with uncontrolled inputs, you don't
// have to put the value in the state.
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('Text field value is: ' + this.state.value);
alert('A name was submitted: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<div>
<input
type="text"
placeholder="Hello!"
onChange={this.handleChange} />
<button onClick={this.handleSubmit}>
Submit
</button>
</div>
<form onSubmit={this.handleSubmit}>
Name:
<input type="text" value={this.state.value} onChange={this.handleChange} />
<input type="submit" value="Submit" />
</form>
);
}
}
ReactDOM.render(
<Form />,
document.getElementById('root')
);
```
[Try it on CodePen.](https://codepen.io/gaearon/pen/pEBOJR?editors=0010)
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.
```javascript
render() {
return <input type="text" defaultValue="Hello!" />;
}
```
Likewise, `<input type="checkbox">` and `<input type="radio">` support `defaultChecked`, and `<select>` supports `defaultValue`.
## Interactive Props
[Try it on CodePen.](https://codepen.io/lacker/pen/oYNzxY?editors=0010)
Form components support a few props that are affected via user interactions:
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.
* `value`, supported by `<input>` and `<textarea>` components.
* `checked`, supported by `<input>` components of type `checkbox` or `radio`.
* `selected`, supported by `<option>` components.
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:
In HTML, the value of `<textarea>` is set via children. In React, you should use `value` instead.
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:
* The `value` of `<input>` or `<textarea>` changes.
* The `checked` state of `<input>` changes.
* The `selected` state of `<option>` changes.
Like all DOM events, the `onChange` prop is supported on all native components and can be used to listen to bubbled change events.
> Note:
>
> 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.
## Advanced Topics
### Why Controlled Components?
Using form components such as `<input>` in React presents a challenge that is absent when writing traditional form HTML. For example, in HTML:
```html
<input type="text" name="title" value="Untitled" />
```
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`.
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:
```javascript
render() {
return <input type="text" name="title" value="Untitled" />;
}
```javascript{2}
handleChange(event) {
this.setState({value: event.target.value.toUpperCase()});
}
```
Since this method describes the view at any point in time, the value of the text input should *always* be `Untitled`.
### Why Textarea Value?
## The textarea Tag
In HTML, the value of `<textarea>` is usually set using its children:
In HTML, a `<textarea>` element defines its text by its children:
```html
<!-- Don't do this in React. -->
<textarea name="description">This is the description.</textarea>
```
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:
```javascript
<textarea name="description" value="This is a description." />
<textarea>
Hello there, this is some text in a text area
</textarea>
```
If you *do* decide to use children, they will behave like `defaultValue`.
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:
### Why Select Value?
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:
```javascript{1}
<select value="B">
<option value="A">Apple</option>
<option value="B">Banana</option>
<option value="C">Cranberry</option>
</select>
```
To make an uncontrolled component, `defaultValue` is used instead.
> Note:
>
> 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
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
### Controlled Input
```javascript{10,23,24}
class Form extends React.Component {
```javascript{4-6,12-14,25}
class EssayForm extends React.Component {
constructor(props) {
super(props);
this.state = {value: ''};
this.state = {
value: 'Please write an essay about your favorite DOM element.'
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
@ -278,85 +101,45 @@ class Form extends React.Component {
}
handleSubmit(event) {
alert('Text field value is: ' + this.state.value);
alert('An essay was submitted: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<div>
<input
type="text"
placeholder="edit me"
value={this.state.value}
onChange={this.handleChange}
/>
<button onClick={this.handleSubmit}>
Submit
</button>
</div>
<form onSubmit={this.handleSubmit}>
Name:
<textarea value={this.state.value} onChange={this.handleChange} />
<input type="submit" value="Submit" />
</form>
);
}
}
ReactDOM.render(
<Form />,
document.getElementById('root')
);
```
[Try it on CodePen.](https://codepen.io/gaearon/pen/JRVaYB?editors=0010)
### Controlled Textarea
```javascript{10,22,23}
class Form extends React.Component {
constructor(props) {
super(props);
this.state = {value: ''};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
Notice that `this.state.value` is initialized in the constructor, so that the text area starts off with some text in it.
handleSubmit(event) {
alert('Textarea value is: ' + this.state.value);
}
## The select Tag
render() {
return (
<div>
<textarea
name="description"
value={this.state.value}
onChange={this.handleChange}
/>
<br />
<button onClick={this.handleSubmit}>
Submit
</button>
</div>
);
}
}
In HTML, `<select>` creates a drop-down list. For example, this HTML creates a drop-down list of flavors:
ReactDOM.render(
<Form />,
document.getElementById('root')
);
```html
<select>
<option value="grapefruit">Grapefruit</option>
<option value="lime">Lime</option>
<option selected value="coconut">Coconut</option>
<option value="mango">Mango</option>
</select>
```
[Try it on CodePen.](https://codepen.io/gaearon/pen/NRmLxN?editors=0010)
### Controlled Select
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:
```javascript{10,20}
class Form extends React.Component {
```javascript{4,10-12,23}
class FlavorForm extends React.Component {
constructor(props) {
super(props);
this.state = {value: 'B'};
this.state = {value: 'coconut'};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
@ -366,173 +149,31 @@ class Form extends React.Component {
}
handleSubmit(event) {
alert('Select value is: ' + this.state.value);
alert('Your favorite flavor is: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
Pick your favorite La Croix flavor:
<select value={this.state.value} onChange={this.handleChange}>
<option value="A">Apple</option>
<option value="B">Banana</option>
<option value="C">Cranberry</option>
<option value="grapefruit">Grapefruit</option>
<option value="lime">Lime</option>
<option value="coconut">Coconut</option>
<option value="mango">Mango</option>
</select>
<button onClick={this.handleSubmit}>
Submit
</button>
</div>
);
}
}
ReactDOM.render(
<Form />,
document.getElementById('root')
);
```
[Try it on CodePen.](https://codepen.io/gaearon/pen/qawrbr?editors=0010)
### Uncontrolled Radio Button
```javascript{25,34,35,44}
class Form extends React.Component {
constructor(props) {
super(props);
this.value = 'B';
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.value = event.target.value;
}
handleSubmit(event) {
alert('Radio button value is: ' + this.value);
}
render() {
return (
<div>
<label>
<input
type="radio"
name="choice"
value="A"
onChange={this.handleChange} />
Option A
</label>
<br />
<label>
<input
type="radio"
name="choice"
value="B"
onChange={this.handleChange}
defaultChecked={true} />
Option B
</label>
<br />
<label>
<input
type="radio"
name="choice"
value="C"
onChange={this.handleChange} />
Option C
</label>
<br />
<br />
<button onClick={this.handleSubmit}>
Submit
</button>
</div>
<input type="submit" value="Submit" />
</form>
);
}
}
ReactDOM.render(
<Form />,
document.getElementById('root')
);
```
[Try it on CodePen.](https://codepen.io/gaearon/pen/ozOPLJ?editors=0010)
### Uncontrolled Checkbox
[Try it on CodePen.](https://codepen.io/lacker/pen/QGWKQP?editors=0010)
```javascript{37,45,46,54}
class Form extends React.Component {
constructor(props) {
super(props);
this.state = {checked: {'A': false, 'B': true, 'C': false}};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
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) {
alert('Boxes checked: ' +
(this.state.checked.A ? 'A ' : '') +
(this.state.checked.B ? 'B ' : '') +
(this.state.checked.C ? 'C' : '')
);
}
render() {
return (
<div>
<label>
<input
type="checkbox"
value="A"
onChange={this.handleChange} />
Option A
</label>
<br />
<label>
<input
type="checkbox"
value="B"
onChange={this.handleChange}
defaultChecked={true} />
Option B
</label>
<br />
<label>
<input
type="checkbox"
value="C"
onChange={this.handleChange} />
Option C
</label>
<br />
<br />
<button onClick={this.handleSubmit}>
Submit
</button>
</div>
);
}
}
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.
ReactDOM.render(
<Form />,
document.getElementById('root')
);
```
## Alternatives to Controlled Components
[Try it on CodePen.](https://codepen.io/gaearon/pen/rrbkWz?editors=0010)
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.

60
docs/uncontrolled-components.md

@ -0,0 +1,60 @@
---
id: uncontrolled-components
title: Uncontrolled Components
permalink: docs/uncontrolled-components.html
---
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.
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.
For example, this code accepts a single name in an uncontrolled component:
```javascript{8,16}
class NameForm extends React.Component {
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit(event) {
alert('A name was submitted: ' + this.input.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
Name:
<input type="text" ref={(input) => this.input = input} />
<input type="submit" value="Submit" />
</form>
);
}
}
```
[Try it on CodePen.](https://codepen.io/lacker/pen/XNWjOj?editors=0010)
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.
### Default Values
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`.
```javascript{6}
render() {
return (
<form onSubmit={this.handleSubmit}>
Name:
<input
defaultValue="Bob"
type="text"
ref={(input) => this.input = input} />
<input type="submit" value="Submit" />
</form>
);
}
```
Likewise, `<input type="checkbox">` and `<input type="radio">` support `defaultChecked`, and `<select>` supports `defaultValue`.
Loading…
Cancel
Save