这是一本书的一个章节,有关 react form 中的受控组件和非受控组件,感谢作者。
你可能已经看过很多文章说“你不应该使用 setState ”,而文档中声称“ refs 是不好的”……这是很矛盾的。很难理解如何“做到正确”,做选择的标准是什么。
到底应该怎样创建 form 呢?
毕竟,form 是很多 web 应用程序的核心,而在 React 中处理 form 似乎有一点基础。
不要怕,让我来想你展示这些方法的不同,以及你应该选择哪一种。
非受控的
非受控输入框就像传统的 HTML 输入框一样:
class Form extends Component {
render() {
return (
<div>
<input type="text" />
</div>
);
}
}
它们记录了你输入的信息,你可以使用 ref 获取它们的值。例如,在按钮的 onClick 处理中:
class Form extends Component {
handleSubmitClick = () => {
const name = this._name.value;
// do something with `name`
}
render() {
return (
<div>
<input type="text" ref={input => this._name = input} />
<button onClick={this.handleSubmitClick}>Sign up</button>
</div>
);
}
}
换句话说,当你需要的时候你需要从表单域中“拉”表单值。当表单提交时这种情况会发生。
这是最简单的实现表单输入的方式,肯定会有这样使用的有效案例:在现实中处理简单的表单,还有学习 react 的时候。
然而他不够强大,所以我们看一下接下来这些受控的输入框。
受控的
一个受控输入框接收它的当前值作为一个属性,并且有一个回调来修改它的值。你可以说这是处理这件事的一种更“ React 的方式”(不代表你总是要使用这种方式)。
<input value={someValue} onChange={handleChange} />
这是很好的……但是这个输入框的值必须要存在于某处的state中。通常,渲染输入框的组件(即表单组件)把它保存在它的state中:
class Form extends Component {
constructor() {
super();
this.state = {
name: '',
};
}
handleNameChange = (event) => {
this.setState({ name: event.target.value });
};
render() {
return (
<div>
<input
type="text"
value={this.state.name}
onChange={this.handleNameChange}
/>
</div>
);
}
}
(当然,它可以保存在另一个组件的状态中,甚至是独立的状态存储,例如 Redux )
每次你输入一个新的字符,handleNameChange 被调用,他接收输入的新值并把它设置到 state 中。
- 开始是一个空字符串——
''。 - 输入一个
a并且handleNameChange获取了一个a并调用setState。输入框接着重新渲染为拥有一个 value 为a。 - 输入
b,handleNameChange获取到值ab并设置到 state 中,输入框再次重新渲染,现在带有属性value="ab"。
这种流程把值的变化“推”给表单组件,因此Form组件始终拥有输入框的当前值,不需要明确地去要值。
这意味着你的数据( state )和 UI(输入框)总是同步的,state 提供值给输入框,输入框请求Form修改当前值。
这也意味着表单组件能够立刻响应输入框变化,例如:
- 立刻反馈,比如校验
- 禁用按钮除非所有表单域都有合法值
- 强制具体的输入格式,如信用卡号
但是如果你不需要这些并且认为非受控的会更简单,那就去吧。
什么使一个元素“受控”
当然,有其他的表单元素:复选框、单选框、下拉框、文本域。
如果通过一个 prop 来设置一个表单元素的 value ,它变为“受控的”,就这样。
每一个表单元素有不同的用来设置 value 的 prop ,下面是一个总结的小表格
| Element | Value property | Change callback | New value in the callback |
|---|---|---|---|
<input type="text" /> | value="string" | onChange | event.target.value |
<input type="checkbox" /> | checked={boolean} | onChange | event.target.checked |
<input type="radio" /> | checked={boolean} | onChange | event.target.checked |
<textarea /> | value="string" | onChange | event.target.value |
<select /> | value="option value" | onChange | event.target.value |
结论
受控和非受控表单字段都有它们的价值,评估具体的场景来选择方法——对你有用的就足够了。
如果你的表单在 UI 反馈方面特别简单,非受控的使用 refs 完全可以,你不必听各种文章说“不好”。
| 特性 | 非受控的 | 受控的 |
|---|---|---|
| 一次性取值(如:提交时) | true | true |
| 提交时校验 | true | true |
| 实时表单域校验 | false | true |
| 按条件禁用提交按钮 | false | true |
| 强制输入格式 | false | true |
| 多输入框控制单个数据 | false | true |
| 动态输入框 | false | true |
而且,这不是一种一劳永逸的决定:你总是可以迁移到受控输入框。从非受控输入迁移到受控输入并不困难。
最后,这是我的有关React中表单的文章的组织清单。