理解「受控组件」与「非受控组件」
本文首发于个人网站:「一名前端攻城师的个人修养」。
受控组件
在 HTML 中,像 input、textarea 这样的表单标签,通常会根据用户的输入来进行更新。即,不同时候使用 value 属性会得到当前状态下用户直接输入的值。
在 React 中,类似的可变状态通常保存在组件的 state 属性中,并且只能通过使用 setState() 来更新。
将两者结合起来,使 React 的 state 成为「唯一数据源」,这就是“受控组件”。受控组件不仅控制着用户输入过程中表单的事件(比如 input 标签的值修改时触发事件),还控制着取值的表单输入元素。
我们将原本的 input 标签,与受控组件式的标签进行对比。
原本的 input 标签形式如下:
<input id="ipt" type="text" value="..."/>
<button id="btn"/>
每次我们改变 input 标签中的值,input 标签的 value 属性总是随着用户输入直接改变:
const btn = document.getElementById('btn')
btn.addEventListener('click', () => {
console.log(document.getElementById('ipt').value) // 此处每次都会直接获取冰显示 input 标签中的值
})
受控组件式的标签:
export default class Demo extends Component {
state = {
value: ''
}
handleChange(event) {
this.setState({
value: event.target.value
})
}
handleClick() {
console.log(this.state.value)
}
render() {
return (
<div>
<input type="text" value={this.state.value} onChange={this.handleChange}/>
<button onClick={this.handleClick}/>
</div>
)
}
}
在上面的示例代码中,我们发现,input 标签内的值有任何改变,都会通过给定的触发事件,直接传入 state 属性中。
button 标签想要打印当前 input 标签的内容,却不是直接从 input 标签中获取数据,而是从 state 属性中获取 input 标签的数据。
尽管在具体的数据上,打印的数据(即 state 属性)与 input 标签的数据是一样的,但是打印的数据来源却不是直接从 input 标签中获得,而是从 state 属性中获得。
这就使得 state 属性称为“唯一数据源”。
这样的操作就类似于 MVVM 框架中的双向绑定(注意:此处并不是说 React 就是 MVVM 框架)。
MVVM 框架中,View 视图中数据发生改变,传递消息给观察者 ViewModel,随后 ViewModel 再将消息传递给 Model。这个过程再反过来执行一遍,便是双向绑定。
此处就与之有些类似:input 标签中的数据发生改变,我们通过 handleChange() 方法将消息传递给受控组件,受控组件在处理该消息后(此处的例子就是直接返回该消息),将新的消息传递给了 View 视图层,也就是此处 input 标签的 value 属性始终绑定了受控组件的 state 状态。
在此处,我们使用了 input 标签进行举例,但是其他的一些表单标签(如 textarea、select)也有类似的受控组件的用法,此处不做具体介绍。
非受控组件
我们可以这样描述非受控组件:非受控组件并不是为每个状态更新都编写新的数据处理函数。
如果理解了上面受控组件所描述的“双向绑定”,那其实我们可以这样来描述非受控组件:
相比于“双向绑定”,非受控组件实际上取消了 ViewModel 将处理后的数据重新发送给 View 视图层这一步,因而变成了“单向绑定”:仅仅是 View 发送消息给了 ViewModel。
(注意:由于 React 并不是 MVVM 框架,因此此处的 ViewModel 层面的描述并不那么精确,但此处只为了用 MVVM 框架中 ViewModel 的概念来更好地阐释受控组件与非受控组件。)
因此,非受控组件我们只需要 ViewModel 获取 View 视图层的数据。想要实现这一点有很多方式:绑定事件使用 event 对象;使用 ref 属性等等。
在 React 组件中,如果想要为非受控组件指定一个初始值,可以使用 defaultValue 属性,而不必使用 value 属性指定初始值。
转载自:https://juejin.cn/post/7074541504869433352