react中使用受控组件和非受控的对比

392 阅读5分钟

受控组件和非受控组件

受控组件、非受控组件都是子组件向父组件传递数据,父组件处理数据。

受控组件

某个组件渲染出的状态受state控制,可消除组件的局部状态,使整个状态可控,这种组件称为受控组件。

  • 需要子组件的数据时直接从state中获取。

受控组件的流程

  1. 初始时,通过state中设置子组件表单的默认值。子组件如<input><select><textearea>,value、checked属性受控制。
  2. 子组件绑定一个change事件,当表单的状态发生变化,就会触发onChange事件,通过e.detail得到value的值,实时更新父组件的state。
  3. 子组件根据新的state重新渲染,实时更新value属性的值,实现双向数据流
state = {
    xx: xxx
}
​
<input 
    type='' 
    value={this.state.xx}
    onChange={e => this.setState({xx: e.target.value})}
/>
​
<select value={abc} onChange={value => this.setState({abc: value})}>
    <option value='' selected=''>xxx选项</option>
    <option value=''>xxx选项</option>
</select>
  • 表单输入的value不直接作为表单的value值,state与表单元素的value绑定在一起,由state控制表单元素的值。

    input中,input框实时输入的值event.target.value设置为input的value。

    select中,被选择option的value值设置为select的value,select与该option匹配。

  • 可以通过受控组件value={}实时收集子组件的数据,无验证;

    也可以通过form对象的方法如validateFields()收集最终子组件的数据,并验证。

受控组件缺点

表单元素的值都是由React组件进行管理,多个受控组件都需要编写事件处理函数,代码臃肿,所以出现了非受控组件。

控制多个表单元素

使用一个事件同时处理多个表单元素。

state = {
    xx: xxx,
    yy: yyy
}
​
fun = (e) => {
    const value = e.target.type === 'checkbox' ? e.target.checked : e.target.value
    this.setState({
        [name]: value
    })
}
​
<input 
    type='' 
    name='xx'
    value={this.state.xx}
    onChange={e => this.setState({xx: e.target.value})}或onChange={this.fun}
/>
  1. 给表单元素添加name属性,名称与状态/数据state中的属性名相同。
  2. setState()更新state,state更新表单元素的值。

非受控组件

父组件通过ref对象获取DOM元素即子组件,父组件可获取子组件中的数据,单向数据流,无验证。

  • 需要子组件的数据时需要获取子组件,再取出子组件中的数据。现用现取。

以下是使用ref的第三种方法获取DOM元素,也可以使用ref前两种方法获取DOM元素。

父组件:
constructor () {
    super ()
    
    this.state = {
        xx: xxx
    }
    
    this.myRef = React.createRef()
}
    
fun = () => {
    const xxx = this.myRef.current.fn() //调用子组件中函数,获得返回值。
    const xxx = this.myRef.current.value //获取子组件表单的value值。
    const xxx = this.myRef.current.state.yy //获取子组件中state的值,不常用。
}
    
render () {
    return (
        <input type='' ref={this.myRef} />
    )
}
​
子组件:
<input />
  1. 父组件constructor函数中创建ref对象:this.myRef = React.createRef()

  2. 子组件添加ref对象到表单属性,自动将标签对象Xxx添加为xx对象的current属性:<Xxx ref={this.myRef} />

    若父组件向子组件传递数据:<Xxx ref={this.myRef} imgs={imgs}/>

  3. 父组件中this.myRef.current得到子组件,非受控组件通过ref对象获取子组件的数据。

    父组件调用子组件中函数获取子组件中数据:this.myRef.current.fn(),最好是调用它的某个方法去取值,通过方法暴露子组件的数据给父组件。

实际项目中

使用Card、Form组件收集表单数据并验证,Form组件中的非受控组件收集表单数据无验证

  • Form.create()()包装该组件,组件可使用getFieldDecorator()()收集数据并实时验证,使用validateFields()验证并获取数据发送请求更新数据。

    第一个参数为标识符;配置对象包含默认值、验证规则。第二个参数为组件标签。

    验证价格时,配置对象中的验证规则包含required、自定义函数。

    验证商品分类时,即Cascader标签时,form对象的getFieldDecorator()()方法中配置对象的默认值为一个数组,可能为空、一个、两个元素,分别代表添加时、修改时的一级列表、修改时的二级列表

  • 受控组件Cascader级联列表。antd组件中的Cascader标签中属性为options={}、loadData={}

    options={}是要展示的数据,options可以实时变化,比如从一维数组到二维数组;loadData={}表示选中某项时的回调函数

    选中某项时触发loadData的回调函数。判断选中项是一级列表还是二级列表。若选中项为一级列表则初始化一级分类options并修正叶子节点;若选中项为二级列表则异步获取该二级列表数组,并遍历数组元素生成二级分类options,并设置为当前一级列表option的children。

    getCategorys是async函数,返回一个promise对象,含有状态和。一级列表则初始化列表,为二级列表则返回该列表数组。根据该promise对象的值判断是一级列表还是二级列表。页面初始化时调用getCategorys函数。

    initOptions()初始化列表。一级列表叶子节点还没确定最终结果,二级列表还没初始化。修改时需要提前初始化二级列表。

  • 非受控组件PicturesWall,内部包含antd组件中的Upload组件、Modal组件。

    Upload标签,属性值为请求路径action=""、请求参数名name=""、可以接收的类型accept=""、要展示的文件列表fileList={}、onChange={}、大图预览onPreview={}

    Modal标签,属性值为visible={}、onCancel={}、footer={null}无确认取消按钮。Modal标签内部包含img标签,Modal标签的作用是大图预览。

    子组件是非受控组件,父组件通过ref对象获取子组件中的数据。