• 浅谈React受控与非受控组件


    背景

    React内部分别使用了props, state来区分组件的属性和状态。props用来定义组件外部传进来的属性, 属于那种经过外部定义之后, 组件内部就无法改变。而state维持组件内部的状态更新和变化, 组件渲染出来后响应用户的一些操作,更新组件的一些状态。如果组件内部状态不需要更新,即没有调用过this.setState, 全部通过props来渲染也是没问题的, 不过这种情况不常见。本文所介绍的内容就是通过props和state的定义来谈谈React的受控组件和非受控组件。

    非受控组件

    顾名思义, 非受控组件即组件的状态改变不受控制.接来下我们以一个简单input组件代码来描述。

    import React, { Component } from 'react';
    import ReactDOM from 'react-dom';
    
    class Demo1 extends Component {
        render() {
            return (
                <input />
            )
        }
    }
    
    ReactDOM.render(<Demo1/>, document.getElementById('content'))
    

    在这个最简单的输入框组件里,我们并没有干涉input中的value展示,即用户输入的内容都会展示在上面。如果我们通过props给组件设置一个初始默认值,<input defaultValue={this.props.value}/>defaultValue属性是React内部实现的一个属性,目的类似于input的placeholder属性。
    ps: 此处如果使用value代替defaultValue,会发现输入框的值无法改变。

    受控组件

    上面提到过,既然通过设置input的value属性, 无法改变输入框值,那么我们把它和state结合在一起,再绑定onChange事件,实时更新value值就行了。

    class Demo1 extends Component {
        constructor(props) {
            super(props);
            this.state = {
                value: props.value
            }
        }
    
        handleChange(e) {
            this.setState({
                value: e.target.value
            })
        }
    
        render() {
            return (
                <input value={this.state.value} onChange={e => this.handleChange(e)}/>
            )
        }
    }
    

    这就是最简单的受控组件模型, 我们可以通过在onChange的回调里控制input要显示的值,例如我们设置input框只能输入数字

    this.setState({
        value: e.target.value.replace(/D/g, '')
    })
    

    现在我们应该完全明白form表单中受控组件和非受控组件的关系。受控组件采取的理念类似于redux的单项数据流理念,即value值是在调用者上更新的。
    那么问题来了。。。

    最后的思考

    现在我们要实现一个简单的input的number类型组件,后面紧跟一个+的button按钮,将输入框内的数字每次加一。所以此处只能按照受控组件的理念来写

    import React, { Component } from 'react';
    
    export default class extends Component {
        constructor(props) {
            super(props);
            this.state = {
                value: props.value
            }
        }
    
        handleChange(e) {
            this.setState({
                value: e.target.value.replace(/D/g, '')
            })
        }
    
        plus() {
            const value = ++this.input.value
            this.setState({
                value,
            })
        }
    
        render() {
            return (
                <div>
                    <input
                        value={this.state.value}
                        onChange={e => this.handleChange(e)}
                        ref={ref => this.input = ref}
                    />
                    <button onClick={() => this.plus()}>+</button>
                </div>
            )
        }
    }
    

    此处功能基本实现完全,但是发现使用此组件之后,面临了另一个问题,我们如何在外部获取到这个输入框的值。一种方法是给组件增加个getValue回调的props,每次value值变化都调用一次getValue,即在handleChange和plus函数里调用,但是存在一个问题是,调用者只能通过getValue被动获取值,而且value值得改变此时还是在组件内部自行变化,不符合受控组件原理,也不满足React单向数据流概念。另一种方法就是将input组件的将要改变的值传到调用者里面,由调用者来决定更不更新组件的值,即此时数据由被调用者input组件生成,传至调用者,调用者判断满足条件后决定更新,再将数据重新传入到被调用者里。而调用者与被调用者彼此之间建立的联系方式通过input组件的props和调用者的state。此时input组件的代码如下

    export default class extends Component {
        constructor(props) {
            super(props);
            this.state = {
                value: props.value
            }
        }
    
        componentWillReceiveProps(nextProps) {
            this.setState({
                value: nextProps.value
            })
        }
    
        handleChange(e) {
            this.props.onChange(e.target.value)
        }
    
        plus() {
            const value = ++this.input.value
            this.props.onChange(value)
        }
    
        render() {
            return (
                <div>
                    <input
                        value={this.state.value}
                        onChange={e => this.handleChange(e)}
                        ref={ref => this.input = ref}
                    />
                    <button onClick={() => this.plus()}>+</button>
                </div>
            )
        }
    }
    

    代码中的this.props.onChange就是调用者内部的函数,通过setState来更新input组件的value值。完整代码已放到github,欢迎指正交流。

  • 相关阅读:
    L1和L2正则
    Python基础(一)
    消息分发
    StringList 自定义快速排序
    Delphi Length函数
    接口的委托实现(通过接口)
    接口委托实现--通过类的对象
    排序
    Socket编程(摘抄)
    Delphi线程同步
  • 原文地址:https://www.cnblogs.com/qingguo/p/5857923.html
Copyright © 2020-2023  润新知