英文原版
个人翻译:
你可能已经看到了很多文章里面说过 "你不应该使用 setState
方法啊",还有一些文档也提出了 "使用 refs 很糟"... 这两种说法看起来十分矛盾,这也导致了很难理解如何正确地使用受控组件和非受控组件和选择它们的标准是什么。
封装表单控件时候就显得很操蛋了。
毕竟,表单控件是许多网页应用的核心。尽管如此,React中的表单处理似乎还有一些基础需要我们去了解。
先不要害怕如何使用它们,让我来带你看看受控组件和非受控组件实现功能之间的区别,以及当你使用它们的时候的一些特点。
非受控组件
非受控组件的输入控件就和传统的 HTML 输入控件一样:
jsx
class Form extends Component {
render() {
return (
<div>
<input type="text" />
</div>
);
}
}
它们可以自行记住你输入的内容,你可以使用一个 ref 对象来获取它们的数值 value。举个例子,使用一个 button 的 onClick 事件处理函数来获取值:
jsx
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 的时候和真实开发场景中的简单表单控件
不过,非受控组件并没有想象中的那么强大,所以接下来让我们看一下受控组件。
受控组件
一个受控组件 需要接收一个 prop 作为当前的值,同时需要传入一个能够更改这个 prop 值的回调函数。你可能会说"这样更符合 React 范式"的方式来实现像上面的表单功能呀!( 但是这样并不意味着你需要经常地去使用受控组件 )
jsx
<input value={someValue} onChange={handleChange} />
这看起来很美好 ... 但是受控组件的值需要存储在像state
这样的地方。
通常,呈现输入的组件(也称为表单组件)会将其保存在其状态中:
jsx
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>
);
}
}
(当然,你也可以把表单的 state 状态存储在其他组件的 state 中;甚至,你还可以把它存储在特定的数据仓库(比如 redux)中)
每次当你输入一个新的字符,handleNameChange
就会被调用。handleNameChange
会携带 input 的最新值来修改 state 中的对应值。
- 一开始,
state.name
的值为''
- 当你输入一个
'a'
,handleNameChange
就会携带着'a'
去调用setState()
,最终会根据'a'
这个值触发受控组件的重渲染。 - 当你输入一个
'b'
,handleNameChange
就会携带着'ab'
去调用setState()
,这个受控组件会随着输入触发重渲染,而这次是根据'ab'
进行重渲染
这个流程就像是"推入"一个值来更新受控表单的组件,所以受控的表单组件总是携带着当前的值,并不需要每次去询问它们的值。
这意味着你的数据和视图总是同步的:数组给视图提供了它所需要的值,视图要求组件给它提供一个当前的值。
这也意味着表单的组件可以立即响应数据进行修改,例如可以通过:
- 就地反馈,例如:表单校验
- 在不满足表单所有的作用域都合法的情况下,把 button 设置为 disabled
- 强制规范输入的格式,比如:信用卡号码的输入
但是如果你还是觉得非受控组件更简单,那你还是用非受控组件吧。
是什么让一个元素变得 "受控"了 (还有啥值得受控的元素)
除了刚才提到的元素之外,当然还有一些元素。比如:checkbox, radio, select, textarea 这些
如果你通过prop设置表单元素的值,它就会变成"受控的"。就酱。
这些表单元素,尽管,它们所需要的配置 prop (传入值,传入事件) 会不同。所以我们还是用一个小小的表格来总结一下:
元素 | 属性值 | 更改的回调函数 | 在回调函数中获取值的方式 |
---|---|---|---|
<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 |
总结一下:
受控组件和非受控组件都有它们各自的优点。评估一下你当前的场景然后再去选择它们! - 这样对于你来说才是足够好的。
如果你开发的表单是非常简单的,那么携带 refs 的非受控组件就完全可以了。你不要再去听那些说非受控组件"不好"的文章了。
特性 | 非受控组件 | 受控组件 |
---|---|---|
一次性的值检索(比如:表单提交) | ✅ | ✅ |
校验提交数据 | ✅ | ✅ |
即时的表单域验证 | ❌ | ✅ |
有条件地禁用提交按钮 | ❌ | ✅ |
强制输入格式 | ❌ | ✅ |
一个 data 及到多个输入组件 | ❌ | ✅ |
动态输入组件 | ❌ | ✅ |