紧接着上一篇介绍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"/>
密码:<input ref={c => this.password = c} type="password" name="password"/>
<button>提交</button>
</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"/>
密码:<input onChange={this.setPassword} type="password" name="password"/>
<button>提交</button>
</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"/>
密码:<input onChange={this.setFormData('password')} type="password" name="password"/>
<button>提交</button>
</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"/>
密码:<input onChange={event => this.setFormData('password', event)} type="password" name="password"/>
<button>提交</button>
</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="点击按钮打印数据"/>
<button onClick={this.showData1}>按钮</button>
<input onBlur={event => console.log(event.target.value)} type="text" placeholder="失去焦点打印数据"/>
</div>
)
}
showData1 = () => {
console.log(this.state.input1);
}
}