React 10 - 表单(Forms)
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:
在React中HTML表单元素的工作方式和其他DOM元素存在些许差异,表单元素可以自主维持其一些内部状态。以下面这个获取姓名的表单为例:
<form>
<label>
Name:
<input type="text" name="name" />
</label>
<input type="submit" value="Submit" />
</form>
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”.
该表单和HTML表单的默认行为一致,当用户提交此表单时浏览器会打开一个新页面。如果这个行为符合需求,如上编码即可完成工作。但是在大多数情况下,使用JavaScript函数便于处理表单提交和访问表单中的用户输入数据。这种需求标准的实现方式叫做“受控组件”。
受控组件(Controlled Components)
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()
.
在HTML中,<input>
,<textarea>
和<select>
等表单元素通常自主维持其自身状态,并基于用户的输入更新。而在React中,可变状态通常由组件的状态属性维持,而且只能通过组件的setState()
方法更新。
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”.
遵循“单一数据源原则”的理念可以将上述两种状态整合。随后React组件渲染表单,也可以在组件中对用户后续输入的内容进行加工处理。输入表单元素的值受React控制叫做“受控组件”。
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:
比如,如果需要在上例中实现表单提交时显示姓名,可以把表单作为“受控组件”来实现:
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('A name was submitted: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input type="text" value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
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
属性之后,其显示值将由this.state.value
决定,以满足React状态的同一数据理念。每次键盘敲击之后会执行handleChange
方法以更新React状态,显示值也将随着用户的输入改变。
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:
对于受控组件来说,每一次状态变化都会伴有相关联的监听方法执行。这使得对用户的输入进行修改和验证都简单直接。比如,如果需要强制用户名的输入都是大写形式,可以这样来写handleChange
方法:
handleChange(event) {
this.setState({value: event.target.value.toUpperCase()});
}
textarea标签(The textarea Tag)
In HTML, a <textarea>
element defines its text by its children:
在HTML中,<textarea>
元素的值以其子元素的形式定义:
<textarea>
Hello there, this is some text in a text area
</textarea>
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:
在React中,<textarea>
的赋值使用value
属性替代。这样一来,表单中<textarea>
的书写方式接近于单行文本输入:
class EssayForm extends React.Component {
constructor(props) {
super(props);
this.state = {
value: 'Please write an essay about your favorite DOM element.'
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('An essay was submitted: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<textarea value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
Notice that this.state.value
is initialized in the constructor, so that the text area starts off with some text in it.
注意this.state.value
在构造函数中进行初始化,所以这些文本一开始就出现在表单中。
select标签(The select Tag)
In HTML, <select>
creates a drop-down list. For example, this HTML creates a drop-down list of flavors:
在HTML中,<select>
会创建一个下拉列表。比如,下面的HTML创建了一个下拉的口味列表:
<select>
<option value="grapefruit">Grapefruit</option>
<option value="lime">Lime</option>
<option selected value="coconut">Coconut</option>
<option value="mango">Mango</option>
</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:
注意Cocount选项默认被选中,这是因为该选项中设置了selected
属性。在React中 ,此属性的作用被select
根标签中的value
属性替代。这样受控组件中在同一处位置即可完成更新,使得受控组件使用更方便。如下例所示:
class FlavorForm extends React.Component {
constructor(props) {
super(props);
this.state = {value: 'coconut'};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('Your favorite flavor is: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Pick your favorite La Croix flavor:
<select value={this.state.value} onChange={this.handleChange}>
<option value="grapefruit">Grapefruit</option>
<option value="lime">Lime</option>
<option value="coconut">Coconut</option>
<option value="mango">Mango</option>
</select>
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
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.
总体上说,这使得<input type="text">
,<textarea>
和<select>
等所有表单元素使用更便捷——使用统一个value
属性即可实现受控组件。
替代受控组件(Alternatives to Controlled Components)
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, an alternative technique for implementing input forms.
有时可能感觉使用受控组件有些枯燥繁琐,因为你需要为每一个可更改的数据提供事件处理器,并且在React中管理所有的输入状态。当你正在将已经存在的代码转化为React形式,或者将一个非React库集成到React应用时,这会变得有些小烦恼。为了解决这个问题,你可以看看非受控组件,实现表单的另一种技术方式。