React受控组件和非受控组件

受控组件和非受控组件主要是用来解决表单组件状态谁来控制的问题。因为用户的输入会反应在界面上,相当于视图的状态发生了变化,而react是通过虚拟DOM比对修改视图的,这里就要决定谁来控制表单组件的状态。由用户直接控制的称为非受控组件,而由react的控制的,称为受控组件。

1. 非受控组件

没有设置value/checked的表单组件,表单数据有DOM元素本身处理
组件自己控制组件的状态,一般父组件会给它一个初始值(通过defaultValue属性)
defaultValue/defaultChecked设置默认值,首次生效,重新渲染后则不生效,有value时也不生效
input元素本身也有defaultValue属性,可以设置或返回其默认值
defaultValue需要在constructor或者componentWillMount设置设置state
其他周期中设置的在render后无法看到默认值

形如:

 <input type="text" />

非受控组件与普通input一致,用户的输入正常显示,由用户来控制组件的状态。

非受控组件通常和ref结合在一起,通过node.value方式来获取dom节点的值,这种方式适合不需要知道实时状态,只需要提交的表单组件。

import React, { Component } from 'react';

class UnControlled extends Component {
    handleSubmit = (e) => {
        console.log(e);
        e.preventDefault();
        console.log(this.name.value);
    }
    render() {
        return (
            <form onSubmit={this.handleSubmit}>
                <input type="text" ref={i => this.name = i} defaultValue="BeiJing" />
                <button type="submit">Submit</button>
            </form>
        );
    }
}

export default UnControlled;

非受控组件的父子组件形式:

//父组件
class Parent extends Component {
    render() {
        return  
            <NumberInput defaultValue=20 />;
    }
}

//子组件NumberInput
class NumberInput extends Component {
    render() {
        return   
            <input type="text" className="NumberInput"
            defaultValue={this.props.defaultValue} />
        
    }
}

2. 受控组件

设置了value/checked的表单组件,其vaule/checked必须是react组件的state,并且有onChange事件来修改state
组件自己不更新值,而是把值传递给父组件,让父组件决定是否更改。

形如:

<input value="this.state.name" onChange="fHandleChange" />

受控组件通常如下:

<input
    type="text"
    value={this.state.value}
    onChange={(e) => {
        this.setState({
            value: e.target.value.toUpperCase(),
        });
    }}
/>

1. 可以通过初始state中设置表单的默认值;
2. 每当表单的值发生变化时,调用onChange事件处理器;
3. 事件处理器通过合成事件对象e拿到改变后的状态,并更新应用的state.
4. setState触发视图的重新渲染,完成表单组件值得更新

受控组件不同表单组件的处理形式:

受控组件的说明:

1. 受控组件会频繁触发change事件,会带来一定的性能损耗,但是影响不大,为了状态的统一,值得一做
2. 受控组件要value和onChange事件配合使用,写起来麻烦,有一些简化手段:
  1. 封装一个InputItem组件,将value和change作为参数
  2. 使用一个onChange方法处理多个Input,内部使用switch来分别处理不同组件的状态

受控组件的父子组件形式:

//父组件
class Parent extends Component {
    constructor(props) {
        super(props);
        this.state = {value: 1};
        this.onChangeHandle = this.onChange.bind(this);
    }
    onChange(event) {
        //处理值
        if (改) {
        this.setState({value: event.target.value});
        }
    }
    render() {
        return  
            <NumberInput value={this.state.value} onChange={this.onChangeHandle} />
        ;
    }
}
//子组件NumberInput
class NumberInput extends Component {
    constructor(props) {
        super(props);
        this.state = {
        value: props.value
        };
        
        this.onChangeHandle = this.onChange.bind(this);
    }
    //接收到新的属性时更新state
    componentWillReceiveProps(nextProps) {
        this.setState({
        value: nextProps.value
        });
    }
    onChange(event) {
        //通知父组件值更新了
        this.props.onChange(event);
    }
    render() {
        return 
            <input type="text" className="NumberInput"
            value={this.state.value}
            onChange={this.onChangeHandle} />
    }
}

3. 混合组件(既支持受控组件也支持非受控组件的组件)

//父组件
//把子组件当受控组件(传递value和onChange):
render() {
    return <NumberInput value={this.state.value} onChange={this.onChangeHandle} />
}
//把子组件当非受控组件(传递defalutValue):
render() {
    return <NumberInput defaultValue={this.state.value} />
}

//子组件NumberInput
class NumberInput extends Component {
    constructor(props) {
        super(props);
        this.state = {
            value: props.value
        };
        this.onChangeHandle = this.onChange.bind(this);
    }
    //接收到新的属性时更新state
    componentWillReceiveProps(nextProps) {
        this.setState({
            value: nextProps.value
        });
    }
    onChange(event) {
        //通知父组件值更新了
        this.props.onChange(event);
    }
    render() {
        let val = {};
        //判断组件的props属性是否有value属性,有的话,是受控组件,没有的话是非受控组件
        this.props['value'] === undefined ?
            (val = {'defaultValue': this.props.defaultValue}) :
            (val = {'value': this.state.value});
        //使用对象和分解运算符来完成不同属性的切换
        return  <input type="text" className="NumberInput"
                {...val}
                onChange={this.onChangeHandle} />;
    }
}

混合组件的基本原则:

1. props.value总是有比内部state.value跟高的优先级
2. 组件中所有的变化都应该同步到内部的state.value,并通过执行props.onChange来触发更新
3. 当组件接受新的props的时候,将props.value反映给state.value
4. 在优先值发生变化后更新组件

有了以上原则,就可以实现一个装饰器,hybridCtrl,来将一个普通组件转换为一个混合组件

@hybridCtrl
class App extends React.Component {
    static propTypes = {
        value: React.PropTypes.any,
    }

    state = {
        _value: '',
    }

    mapPropToState(controlledValue) {
        // your can do some transformations from `props.value` to `state._value`
    }

    handleChange(newVal) {
        // it's your duty to handle change events and dispatch `props.onChange`
    }
}

结论

1、为什么我们需要混合组件?

我们应该创建同时受控和非受控的组件,就像原生的元素那样。

2、混合组件主要思想是?

同时维护props.value和state.value的值。props.value在展示上拥有更高的优先级,state.value代表着组件真正的值。

混合组件的一个例子(作者是一村又一庄):

1、支持传入默认值;
2、可控:组件外部修改props可改变input组件的真实值及显示值;
3、非可控:输入框中输入值,可同时改变input组件的真实值及显示值。

示例代码地址:https://github.com/abell123456/hybrid-component

4. 受控组件和不受控组件的使用区别

5. 表单组件的几个重要属性

1.状态属性

React的form组件提供了几个重要的属性,用来显示组件的状态

value: 类型为text的input组件,textarea组件以及select组件都借助value prop来展示应用的状态
checked: 类型为radio或checkbox的组件借助值为boolean类型的selected prop来展示应用的状态
selected: 该属性可作用于select组件下面的option上,React并不建议这种方式表示状态.而推荐在select组件上使用value的方式

2.事件属性

当状态属性改变时会触发onChange事件属性.受控组件中的change事件与HTML DOM中提供的input事件更为类似

 

 

参考:https://segmentfault.com/a/1190000012404114
   https://blog.csdn.net/jianruoche/article/details/79238831
         https://blog.csdn.net/fendouzhe123/article/details/52121704
         https://www.cnblogs.com/aichenxy/p/6758004.html

posted @ 2018-09-06 18:04  全玉  阅读(730)  评论(0编辑  收藏  举报