受控组件与非受控组件

7 分钟读完

前言

写这篇博客的原因很蛋疼,上周六阿里校招的面试电话中,面试官问了我什么是受控组件和非受控组件,我回答错的有点离谱……

要说答不上来的原因,是在我接触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]

不管通过什么方式,都是获取表单元素的引用,然后获取表单元素的值,将这些值提交出去。

更新时间: