React受控组件、非受控组件、高阶函数与柯里化

紧接着上一篇介绍refs的案例React三大属性之props与refs简介,本文先进行一些优化,然后引入一些新的概念。优化的出发点是因为React官网说不要过多使用refs,因此能不用refs就不用refs。以第二个输入框为例,其事件onBlur的回调函数可以简写为:

<input onBlur={event => console.log(event.target.value)} type="text" placeholder="失去焦点打印数据"/>

这里的onBlur的值一定要为一个函数,这里的函数是箭头函数的形式,入参是事件event,用event.target获取触发事件的节点,即当前的输入框,然后再取其值value进行打印。这里之所以能够简写,是因为事件的触发节点和要获取数据的节点是一个同一个节点,因此可以不用refs。

考虑如下需求:编写登录页面,提交表单后,打印用户名和密码,不进行跳转和刷新,即使表单的action属性存在值,实现如下:

class MyDemo extends React.Component{
    handleSubmit = (event) => {
        event.preventDefault();
        console.log(this.username.value);
        console.log(this.password.value);
    }

    render() {
        return (
                <div>
                    <form action="https://www.baidu.com" onSubmit={this.handleSubmit}>
                        用户名:<input ref={c => this.username = c} type="text" name="username"/> &nbsp;
                        密码:<input ref={c => this.password = c} type="password" name="password"/> &nbsp;
                        <button>提交</button> &nbsp;
                    </form>
                </div>
            )
    }
}

这里为了让表单不执行action,使用了event.preventDedault()方法来阻止提交。两个输入框的ref属性为箭头函数,里面设置当前实例对象的username和password的属性值,分别为这两个输入框节点,在表单的onSubmit函数中打印出节点值。这里的输入框最好带上name属性,养成好习惯。以上成为非受控组件,即现用现取,onSubmit方法中需要使用输入框的值,那么就直接获取即可。以下代码通过将输入框节点设置在组件的状态state属性中,实现了受控组件(功能一样):

class MyDemo extends React.Component{
    setUsername = (event) => {
        this.setState({username:event.target.value});
    }

    setPassword = (event) => {
        this.setState({password:event.target.value});
    }

    handleSubmit = (event) => {
        event.preventDefault();
        console.log(this.state.username);
        console.log(this.state.password);
    }

    render() {
        return (
                <div>
                    <form action="https://www.baidu.com" onSubmit={this.handleSubmit}>
                        用户名:<input onChange={this.setUsername} type="text" name="username"/> &nbsp;
                        密码:<input onChange={this.setPassword} type="password" name="password"/> &nbsp;
                        <button>提交</button> &nbsp;
                    </form>
                </div>
            )
    }
}

所谓受控组件,不再是随取随用,而是将需要用的值先存起来,比如存在state中,此时需要注意的是,一旦值变化,也需要立即修改state中存储的值,用的时候再从state中取出来即可。这种方法避免了使用refs,这里的onChange事件函数是设置state属性的状态,但是用户名和密码这两个输入框的onChange函数非常相似,可以继续优化成一个函数:

class MyDemo extends React.Component{

    handleSubmit = (event) => {
        event.preventDefault();
        console.log(this.state.username);
        console.log(this.state.password);
    }

    setFormData = (dataType) => {
        return (event) => {
           console.log(dataType, event.target.value);
           this.setState({[dataType]: event.target.value});
        }
    }

    render() {
        return (
                <div>
                    <form action="https://www.baidu.com" onSubmit={this.handleSubmit}>
                        用户名:<input onChange={this.setFormData('username')} type="text" name="username"/> &nbsp;
                        密码:<input onChange={this.setFormData('password')} type="password" name="password"/> &nbsp;
                        <button>提交</button> &nbsp;
                    </form>
                </div>
            )
    }
}

这里的onChange属性值必须为一个函数,因此大括号里的返回值一定要为一个函数,即setFormData函数的返回值为一个函数,该函数就是设置state属性值。注意设置state属性值时,由于key是一个变量,不能直接写dataType,而要使用中括号。setFormData的入参为数据类型,即当前节点(是用户名还是密码,后面作为state属性的key),setFormData函数的返回值函数(return后面的箭头函数)的入参为事件,从中获取触发事件的节点的值。

注意这里的setFormData就是一种高阶函数,所谓高阶函数,就是入参或返回值也是函数的函数,比如Promise、setTimeout、数组的map方法等。setFormData同时也实现了柯里化,即函数调用通过返回函数的形式,实现了参数在最后统一处理的结果。

总之,之所以要用refs,大多是想获取该节点的(文本)值,因为refs的回调函数大多是设置当前实例对象的某个属性为该节点,而onChange函数可以实现将该节点的值实时地存入组件的属性state中,因此后续其他节点如果需要用到则可以从state属性中取出来,从而实现代替refs的功能。

上述代码如果觉得难以理解,还可以继续改写。既然onChange属性的值必须为一个函数,其实没必要新写一个返回函数的函数,直接在onChange属性值中写一个箭头函数即可:

        class MyDemo extends React.Component{

            handleSubmit = (event) => {
                event.preventDefault();
                console.log(this.state.username);
                console.log(this.state.password);
            }

            setFormData = (dataType, event) => {
                this.setState({[dataType]: event.target.value});
            }
            
            render() {
                return (
                        <div>
                            <form action="https://www.baidu.com" onSubmit={this.handleSubmit}>
                                用户名:<input onChange={event => this.setFormData('username', event)} type="text" name="username"/> &nbsp;
                                密码:<input onChange={event => this.setFormData('password', event)} type="password" name="password"/> &nbsp;
                                <button>提交</button> &nbsp;
                            </form>
                        </div>
                    )
            }
        }

上篇文章React三大属性之props与refs简介中的两个输入框用到的refs都可以被替换,如下是替换refs后的代码:

class MyDemo extends React.Component{
    render() {
        return (
            <div>
                <input onChange={event => this.setState({input1:event.target.value})} type="text" placeholder="点击按钮打印数据"/> &nbsp;
                <button onClick={this.showData1}>按钮</button> &nbsp;
                <input onBlur={event => console.log(event.target.value)} type="text" placeholder="失去焦点打印数据"/>
            </div>
        )
    }
    showData1 = () => {
        console.log(this.state.input1);
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值