目录
0、关于受控组件和非受控组件
以表单作为简单例子。
组价的受控和非受控大概可以理解为:
用户输入的数据或者是选择的操作实时被监视,并将值实时地更新到 state 中,在需要使用时,可以直接到 state 中获取,就是受控的;
如果用户输入的数据或各种操作的结果在需要收集时是利用 ref 绑定了元素、通过 refs 的方式来获取的,那就是非受控组件;
是否受控,大概可以理解为数据是否实时 “受监控”。
由于非受控组件会使用到 ref ,且可能会使用到很多次,所以多少会存在一些性能上的问题,在数据需要收集的时候,可以多考虑使用受控组件来完成。
1、非受控组件
const {Component, createRef} = React;
class Form extends Component{
pw = createRef();
submitForm = (event)=>{
// 阻止默认时事件的发生(即表单提交的默认跳转)
event.preventDefault();
// 需要用的时候才获取两个输入框中用户输入的内容
console.log(this.pw.current.value, this.userNameNode.value);
}
render(){
return(
<div>
{ /* 不填action默认提交到当前地址 */ }
<form action="" onSubmit={this.submitForm}>
{ /* 内联回调函数的形式 */ }
userName: <input ref={cn =>{ this.userNameNode = cn }} type="text" name="username"/><br/>
{ /* React.createRef()的形式 */ }
password: <input ref={this.pw} type="password" name="pw" /><br/>
<input type="submit" value="submit input"/>
</form>
</div>
)
}
}
ReactDOM.render(<Form/>, document.querySelector('#test'));
2、受控组件
const {Component, createRef} = React;
class Form extends Component{
state = { // 预设需要收集的字段
username:'',
pw:'',
gender:'woman' // 设置性别为option的第一个,select默认选中第一个
}
render(){
return(
<div>
<form action="" onSubmit={this.submitForm}>
userName: <input type="text" name="username" onChange={this.unChange}/><br/>
password: <input type="password" name="pw" onChange={this.pwChange}/><br/>
gender: <select name="gender" onChange={this.changeGender}>
<option value="woman">woman</option>
<option value="man">man</option>
</select><br/>
{
// 使用默认的reset清除表单内容
// <input type="reset" value="reset"/>
}
<button onClick={this.resetForm}>reset Form</button>
<input type="submit" value="submit input"/>
</form>
</div>
)
}
submitForm = (event)=>{
// 阻止默认时事件的发生(即表单提交的默认跳转)
event.preventDefault();
// 需要使用时,直接在state中拿备份存储好的数据,不需要再使用ref绑定的形式
console.log(this.state, this.state.gender);
}
// 实时更新state中的内容
unChange = (event)=>{
this.setState({ username:event.target.value });
}
pwChange = (e) =>{
this.setState({ pw:e.target.value });
}
changeGender = (e)=>{
this.setState({ gender:e.target.value });
}
}
ReactDOM.render(<Form/>, document.querySelector('#test'));
3、数据双向绑定的简单实现
const {Component, createRef} = React;
class Form extends Component{
state = {
value:0
}
render(){
return(
<div>
{ /* label 标签中的for 要改写成 htmlFor,其值对应着指定输入框的id */ }
<label htmlFor="inpnum">
请输入一个不大于10的数
{ /*
value 绑定 state 中的value,
onChange 绑定一个自定义方法,监视用户的输入
当用户有输入时,state中的value会实时更新 (用户触发)
当用户输入的数大于 10 时,系统自动重置为0 (系统触发)
*/ }
<input type="text" id="inpnum" value={this.state.value} onChange={this.numberChange}/>
</label>
</div>
)
}
numberChange = (e)=>{
if(e.target.value > 10){
this.setState({ value:0 });
return;
}
// 少了以下这句,用户无法在输入框进行输入
this.setState({ value:e.target.value });
}
}
ReactDOM.render(<Form/>, document.querySelector('#test'));
4、高阶函数及函数的柯里化优化数据收集
(4-1)什么是高阶函数?
简答:满足以下条件之一的函数可以被称为是高阶函数:
① 该函数接收另外的函数作为自己的参数;
② 该函数返回的内容是一个函数;
常见的高阶函数举例:setTimeout、Promise、Array.map、Array.filter ......
(4-2)什么是函数的柯里化?
简答:把接受多个参数的函数变换成接受一个单一参数的函数,并且返回接受余下的参数且返回结果的新函数
函数的柯里化举例:
将三个数值参数相加作为返回值的函数的普通写法:
(控制台换行输入:Shift + Enter)
函数柯里化改造:
等价于:
(4-3)改造用户数据收集方式
为每一项用于收集用户数据的标签都添加一个 onChange 事件,为他们都绑定一个函数updateValue,这个函数加上小括号,把当前标签所收集的用户数据名称作为参数传递过去。
这样,在 React 初次渲染该组件的时候,遇到这些标签都会执行一遍这个函数,并传入数据项名称。
这个函数会返回一个匿名的函数给这样的每一个标签的 onClick 事件作为回调执行,所以这个匿名的函数会收到一个默认的参数,即事件对象。
有了事件对象,而且这个事件对象就是触发 onClick 事件的源,那就可以直接获取到更新后的新状态(值)。同时,我们也拿到了当前项的数据名称(每一个匿名的回调函数里面都有一个数据:type,表示当前项对应的数据名称,如 userName),所以就可以利用 setState 去更新当前数据名称所对应的值了。
以第一项(收集userName,数据项名称为userName)为例,也可以理解为 onChange 事件触发时,会调用函数 this.updateValue('userName')(eventObj) 即 (this.updateValue('userName'))(eventObj)。
这样,只需要一个更具有普遍性的高阶函数,就能完成对表单众多数据的收集和更新了。
const {Component, createRef} = React;
class Form extends Component{
state = {
userName:'',
oldPassword:'',
newPassword:'',
checkCode:''
}
render(){
return(
<div>
userName: <input type="text" onChange={this.updateValue('userName')}/><br/>
oldPassword: <input type="password" onChange={this.updateValue('oldPassword')}/><br/>
newPassword: <input type="password" onChange={this.updateValue('newPassword')}/><br/>
checkCode: <input type="text" onChange={this.updateValue('checkCode')}/><br/>
<input type="submit" value="submit"/>
</div>
)
}
// 每一个数据项在渲染时都会调用这个方法,把该方法返回的函数赋给 onChange
// 在数据项的内容被更新时,会调用这个返回的函数
// updateValue 接收到的参数是调用该方法时传递过来的参数
updateValue(type){
// 里面这个返回的函数是供 onChange 调用的,接收到的参数是 event 事件对象
return (event)=>{
this.setState({
// 当拥有键名的时候,更新键所对应的值的方法 [keyName]:newval
[type]:event.target.value
})
}
}
}
ReactDOM.render(<Form/>, document.querySelector('#test'));
5、再做优化(不使用柯里化函数)
写成回调函数的形式,该回调函数再调用指定的函数。
const {Component, createRef} = React;
class Form extends Component{
state = {
userName:'',
oldPassword:'',
newPassword:'',
checkCode:''
}
render(){
return(
<div>
{
// 在花括号中 将一个匿名函数赋给 onChange 事件作为回调
// 该匿名函数由 onChange 调用,会收到事件对象作为参数
// 该匿名函数中,再调用一个指定的函数,并将当前数据项名称和事件对象作为参数
// 更新当前数据项中的值的任务由该制定的函数完成
}
userName: <input type="text" onChange={ (event)=>{this.updateValue('userName', event)} }/><br/>
oldPassword: <input type="password" onChange={ (event)=>{this.updateValue('oldPassword', event)} }/><br/>
newPassword: <input type="password" onChange={ (event)=>{this.updateValue('newPassword', event)} }/><br/>
checkCode: <input type="text" onChange={ (event)=>{this.updateValue('checkCode', event)} }/><br/>
<input type="submit" value="submit"/>
</div>
)
}
updateValue(type, event){
this.setState({
[type]:event.target.value
})
}
}
ReactDOM.render(<Form/>, document.querySelector('#test'));