表单(一)
受控 && 非受控组件
在react中,提出了受控与非受控组件,对比我们之前的做法,非受控组件就相当于我们之前的版本,没有默认值,或者一般将初始状态进行设定,而受控组件指的是为其指定默认值以及为事件绑定与控件对应的值。
- 案例代码链接
import React from "react";
class Form extends React.Component {
constructor(props) {
super(props);
this.state = {
value: "color"
};
}
handleChange(e) {
this.setState({
value: e.target.value
});
}
render() {
const { value } = this.state;
return (
<div class="app">
<h2>form受控组件</h2>
<input defaultValue={value} onChange={this.handleChange.bind(this)} />
<h2>form非受控组件</h2>
<input name="username" />
</div>
);
}
}
export default Form;
复制代码
在react建议中,一般都建议我们写受控组件,这样应用的状态以及数据可以实时把握,并根据需要进行渲染。
select 与 checkbox等默认值的设置
另外一点不得不提的是一些表单控件的默认值的设置,在传统的控件写法中我们是直接将选中项的值设置为checked,selected,而在react中,我们是通过设置其父标签,也就是select标签的值来实现的。
并且这种思想无论在react组件还是vue组件中都很常见,也不局限于表单组件,我们习惯了将一个容器内包含着若干元素的组件的默认值通过配置容器属性来实现,而不是每个具体组件设置值。
<select>
<option value="男">男</option>
<option value="男" selected>女</option>
</select>
<select value='女'>
<option value="男">男</option>
<option value="女">女</option>
</select>
复制代码
其实你如果是上面的这种写法,说明你还没有完全从传统的写法中脱离出来,结合react的设计思想,我们应该把一切符合数据概念的数据放到状态state里去管理。有了这种思想,我们才能更好的用好这个框架。
// 构造器
constructor(props){
super(props);
this.state = {
sexArr :[{
value:'0',
label:'男'
},{
value:'1',
label:'女'
}
],
value:'0'
}
}
render(){
let {sexArr, value} = this.state;
return(
<select value={value}>
{sexArr.map(item=>(<option key={item.value} value={item.value}>{item.label}</option>))}
</select>
)
}
复制代码
受控组件是为什么?数据改造还是获取数值
可能通过受控组件我们发现文本的获取值以及设置值比原先复杂多了,因为原先的方式,非受控的时候,我们就可以直接改变数值,获取的时候直接获取就可以了,加了受控反而增加很多累赘的代码,这里进行下说明。
增加了事件监听并没有增加更多的代码
我们在每个表单组件中增加了事件监听,让其事件改变后的值赋值到我们的状态机,而且是必须这样操作,这个目的是为了让数据直接存储到我们的状态机里,然后进行统一的读取方便其他需要时调用。
这里对比下我们之前的开发方式,比如说jq框架下,我们虽然不用写时间监听改变值,但没有统一管理变量的思想时,我们总是每次用到数值的时候都需要写获取元素,然后获取元素的值,这部分的代码我们现在不需要了。
所以这里与原来的方式对比可以理解为下面几点重要的区别:
- 增加了事件监听,多写了很多事件监听函数,原先不用写
- 增加了state值的赋值,方便了需要时方便的读取,原先不用写
- 方便了数据的获取,需要时统一通过解构从state中直接获取,原先需要获取元素获取属性值
- 方便了数据的统一管理,原先是没有统一管理,数据管理比较乱
- 减少了反复根据需要设置控件的选中情况,原先需要根据需求不断的进行手动改变控件的值
增加了数据改造的入口
通过显性的增加事件监听,我们可以根据需求灵活的对数据进行验证或者数据改造,在应用日益复杂的今天,这方面的需求还是非常多的,我们获取数据不仅仅是为了获取数据,或者说不仅仅是直接使用这个数据,也可能是需要改造这个数据,或者基于这个数据去生成其他数据,或者执行其他函数,变为受控组件给了我们有这些需求时方便操作的时机和入口和设计方案。
事件绑定的性能优化
虽然受控组件意味着增加很多的事件监听,但这仍然是react推荐的方式,我们可以通过以下的方式简化这个认为复杂的过程。
redux/flux
这两个数据共享的技术方案可以很方便的解决这个问题,将数据进行额外的管理,这里不做更多介绍。
事件绑定直接优化
其实基于事件绑定,很多人认为繁琐,或者说就是繁,还是因为对函数的提炼思维不够,我们针对事件的绑定需要尽可能的抽象,不要针对每一个具体的组件去绑定事件写具体函数,而要尽可能的抽象,尤其对于同一类事件类型。下面代码说明。
class Form extends React.component{
constructor(props){
super(props);
this.state = {
inputValue:'',
sex:1,
sexArr :[{
value:'0',
label:'男'
},{
value:'1',
label:'女'
}
],
}}
handleInputChange(e){
this.setState({
inputValue:e.target.value
})
}
handleSexChange(e){
this.setState({
inputValue:e.target.value
})
},
render(){
let {sexArr, sex,inputValue} = this.state;
return(
<div>
<input value={inputValue} onChange={this.handleInputChange.bind(this)} name="name">
<select value={sex} onChange={this.handleSexChange.bind(this)} name="sex">
{sexArr.map(item=>(<option key={item.value} value={item.value} name="sex">
{item.label}</option>))}
</select>
</div>
)
}
}
复制代码
观察发现,两者监听的同一类事件,并且赋值也是类似的,那么我们直接增加事件监听的通用性即可,减少事件监听与具体改变值的耦合。(下面只提供变动的代码)
handleChange(type,e){
const {value} = this.state;
this.setState({
[type]:value
})
render(){
let {sexArr, sex,inputValue} = this.state;
return(
<div>
<input value={inputValue} onChange={this.handleChange.bind(this,'inputValue')} name="name">
<select value={sex} onChange={this.handleChange.bind(this,'sex')} name="sex">
{sexArr.map(item=>(<option key={item.value} value={item.value} name="sex">
{item.label}</option>))}
</select>
</div>
)
}
复制代码