受控组件与非受控组件
前言
写这篇博客的原因很蛋疼,上周六阿里校招的面试电话中,面试官问了我什么是受控组件和非受控组件,我回答错的有点离谱……
要说答不上来的原因,是在我接触react的这一个月以来,使用的一直是ant design UI库的接口处理表单,而没有考虑到脱离了库的表单如何处理。闲话不说了,开始进入正题。
受控组件和非受控组件概念出现的前提
在react中,我们常用JSX来写元素,在JSX中我们可以像写原生html一样写表单元素,比如这样:
funtion myConponent(props) = {
return (
<form>
<input type="text"/>
<button type="submit">提交</button>
</form>
)
}
这样写在react中是有问题的:
当我们点击按钮的时候,表单会提交,会跳转到一个新界面
而react应用往往都是单页应用,因此,跳转到其他页面这种方式肯定是不合适的。替代处理方式应该是:
给提交按钮绑定一个点击事件监听器,然后在事件处理函数中阻止默认的提交行为。然后获取表单中的值,将这些值发送给某个服务器。
这时就需要处理一个问题,我们如何在事件处理函数中获取表单元素的值,通过DOM操作读取在react中肯定是不合理的:
- react本来就是避免DOM操作的,再使用DOM操作不符合react原则
- 表单元素的数据由元素自己持有,自己更新,外部是不能去控制它的状态的,这样是不符合react的单向数据流的思想的。
因此出现了受控组件和非受控组件这两种解决方案。
受控组件
受控组件的真实含义是:受(状态)控制的组件。
这种解决方案是,将所有表单的value都绑定在state中 ,并且表单值的改变(onChange事件),都会触发setState方法去更新state中存的表单值。这样就“接管”了表单元素的数据和数据更新。
这是一个简单的例子:
class MyForm extends React.Component{
constructor(props){
super(props);
this.state = {
name: null,
age: null,
}
}
handleNameChange = (e) => {
//在这里我们可以验证数据
this.setState({
name: e.target.value
})
}
handleAgeChange = (e) => {
this.setState({
age: e.target.value
})
}
handleSubmit = (e) => {
e.preventDefault();
//执行表单提交操作
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<input name="name" type="text" value={this.state.name}
onchange={this.handleNameChange}
/>
<input name="age" type="number" value={this.state.age}
onchange={this.handleAgeChange}
/>
<button type="submit">提交</button>
</form>
)
}
}
当然,在这个例子中,我们没有必要为每一个表单元素都设置变化处理函数,而可以写一个公用的,然后通过event.target.name
来判断,然后分类处理。
受控组件的不足
受控组件这种处理方式,需要我们对每个表单元素的各种改变情况都要手动处理,这样会变得比较复杂,因此可以使用react中的另一种解决方式:非受控组件
非受控组件
在了解非受控组件之前我们要先了解一下react中的ref,可以看这篇文章refs使用。
简单的来说,在一个组件中,我们可以通过给
render()
中的元素设置ref属性,获取这个元素的引用(其实是对元素实例的引用,因为组件装载后元素已经得到实例了)。然后在父组件中可以直接去通过引用操作这个元素。
非受控组件的思想是:
不去写一大堆事件监听表单元素的值的变化,也不在state中存储每个表单元素的值,而是直接获取表单元素的引用。
这是一个例子:
class MyForm extends React.Component{
handleSubmit = (e) => {
e.preventDefault();
console.log(this.inputRef.value);
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<input type="text" ref={input => this.input = input}/>
<button type="submit">提交</button>
</form>
)
}
}
这种方式很像在原生js中通过AJAX处理表单,在原生js中我们获取表单元素的引用的方式是:
- 通过DOM查询,如
document.querySelector()
,document.getElementById()
… - 通过
document.forms[form_name].elements[field_name]
不管通过什么方式,都是获取表单元素的引用,然后获取表单元素的值,将这些值提交出去。