在前端与用户发生最多的交互行为都产生在表单里,在React中表单组件不同于其他组件,因为它们会缓存用户的录入信息和录入状态,由此也带来了React对表单处理的一些特殊性。你是不是经常会在学习或者React中对于使用表单有所疑惑,有很多小伙伴会陷入表单的坑中,并走不出来。技术胖专门写一篇文章来讲述React中表单的使用。
在学习这节课之前,你必须学习我的上一套课程《React入门与组件》 。
第1节:表单的事件响应和bind复用
事件响应
表单组件可以通过设置onChange()回调函数来监听组件变化。当用户的交互行文导致一下变化时,onChange()被执行并通过浏览器做出响应。
- <input>或<textarea>的value发生变化。
- <input>的checked状态改变。
- <option>的selected 状态改变。
bind复用
bind方法为事件相应函数增加一个参数,事件响应函数通过该参数识别事件源。
我们作一个文本框,并使用bind进行复用,我希望你自己可以亲自动手敲一下代码,加深对bind复用的理解。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>React-props</title>
<script src="./common/react.js"></script>
<script src="./common/react-dom.js"></script>
<script src="http://cdn.bootcss.com/babel-core/5.8.38/browser.min.js"></script>
</head>
<body>
<div id="demo"></div>
<script type="text/babel">
var MyForm = React.createClass({
getInitialState:function(){
return{
username:'',
gender:'man',
checked:true
}
},
handleChange:function(name,event){
var newState={};
newState[name]=name=="checked"?event.target.checked:event.target.value;
this.setState(newState);
console.log(newState);
},
submitHandler:function(e){
e.preventDefault();
var is = this.state.checked?'是':'不是';
var gender= this.state.gender == "man" ? "帅哥":"美女";
alert(this.state.username+ is + gender +'.' );
},
render:function(){
return (<form onSubmit={this.submitHandler}>
<label htmlFor="username">情输入您的姓名:</label>
<input type="text" name="username" onChange={this.handleChange.bind(this,"username")} value={this.state.username} id="username" />
<br/>
<label htmlFor="checkbox">是或否:</label>
<input type="checkbox" value="是否" name="checked" id="checkbox" onChange={this.handleChange.bind(this,"checked")} checked={this.state.checked} />
<br/>
<label htmlFor="username">请选择</label>
<select name="gender" onChange={this.handleChange.bind(this,"gender")} value={this.state.gender}>
<option value="man">帅哥</option>
<option value="woman">美女</option>
</select>
<br/>
<button type="submit">提交</button>
</form>)
}
});
ReactDOM.render(<MyForm/>,document.getElementById('demo'))
</script>
</body>
</html>
上边的代码精髓就在handleChange里边,所以一定要注意里边的写法,因为在React中这非常常用。
handleChange:function(name,event){
var newState={};
newState[name]=name=="checked"?event.target.checked:event.target.value;
this.setState(newState);
console.log(newState);
},
另外需要注意的是在<label>标签里的for不能在正常使用了,而是要写成htmlFor。
第2节:React表单name复用
name复用方式直接读取表单的属性值,比bind写法少一个参数(React中事件响应函数会自动绑定this)。其原理是在所有的标签中设置统一的name属性,并将这个属性值对应为state属性,在事件响应函数中通过读取表单的name值获得state属性,从event.target.value获取用户输入的值(check控件稍有不同),要求所有相关的标签(包括input标签)都要统一设置name属性。–引用《React前端技术与工程实践》
也许你会对上边的话不理解,其实这种方法看起来更简单,就是每个标签加一个name,然后判断name来进行state的更改。但是我不建议这样使用,因为为每个标签增加一个name属性值并不友好。下面我们修改昨天的代码把bind复用形式改为name复用形式。
其实改的就几个点:改变handleChange的方法,然后改变触发方法,直接写成this.handleChange。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>React-props</title>
<script src="./common/react.js"></script>
<script src="./common/react-dom.js"></script>
<script src="http://cdn.bootcss.com/babel-core/5.8.38/browser.min.js"></script>
</head>
<body>
<div id="demo"></div>
<script type="text/babel">
var MyForm = React.createClass({
getInitialState:function(){
return{
username:'',
gender:'man',
checked:true
}
},
handleChange:function(event){
var newState={};
newState[event.target.name]=event.target.name=="checked"?event.target.checked:event.target.value;
this.setState(newState);
console.log(newState);
},
submitHandler:function(){
e.preventDefault();
var is = this.state.checked?'是':'不是';
var gender= this.state.gender == "man" ? "帅哥":"美女";
alert(this.state.username+ is + gender +'.' );
},
render:function(){
return (<form onSubmit={this.submitHandler}>
<label htmlFor="username">情输入您的姓名:</label>
<input type="text" name="username" onChange={this.handleChange} value={this.state.username} id="username" />
<br/>
<label htmlFor="checkbox">是或否:</label>
<input type="checkbox" value="是否" name="checked" id="checkbox" onChange={this.handleChange} checked={this.state.checked} />
<br/>
<label htmlFor="username">请选择</label>
<select name="gender" onChange={this.handleChange} value={this.state.gender}>
<option value="man">帅哥</option>
<option value="woman">美女</option>
</select>
<br/>
<button type="submit">提交</button>
</form>)
}
});
ReactDOM.render(<MyForm/>,document.getElementById('demo'))
</script>
</body>
</html>
通过两节课的学习,我们可以了解两种表单的操作方式,这两种方式尽量掌握,个人感觉第一种在项目中用的多一些。
第3节:React表单-可控组件
可控组件和不可控组件:
在React中的input标签是有些小坑的,input本身就有自己的缓存机制,然后React的State也有缓存机制。这两种缓存机制我们在编码中是要进行取舍的。将input中的value绑定到state的React组件就是可控组件,反之则是不可控组件。
可控组件:
在render()函数中设置了value的<input>是一个功能受限的组件,渲染出来的HTML元素始终保持value属性的值,即使用户输入也不会改变。
var MyForm = React.createClass({
render:function(){
return(
<div>
<input type="text" value="jspang" />
</div>
)
}
});
这时候你在浏览器中打开的Jspang的值是不可变的,甚至连删除都删除不了,这是由React的渲染策略决定的。如果要写成功能正常和可用性组件,我们需要编写onChange事件,并将value绑定到state中。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>React-props</title>
<script src="./common/react.js"></script>
<script src="./common/react-dom.js"></script>
<script src="http://cdn.bootcss.com/babel-core/5.8.38/browser.min.js"></script>
</head>
<body>
<div id="demo"></div>
<script type="text/babel">
var MyForm = React.createClass({
getInitialState:function(){
return {value:'jspang'}
},
handleChange:function(event){
this.setState({value:event.target.value});
console.log(this.state.value)
},
render:function(){
return(
<div>
<input type="text" value={this.state.value} onChange={this.handleChange} />
</div>
)
}
});
ReactDOM.render(<MyForm/>,document.getElementById('demo'))
</script>
</body>
</html>
在情况允许的条件下,我们应该优先考虑编写可控组件。
可控组件的有点:
- 符合React单向数据流特性,即从state流向render输出的结果。
- 数据存储在state中,便于访问和处理。
第4节:React表单-不可控组件
在input标签中不把value绑定到state上的就是不可控组件,它的数据不合state对应,所以在开发时会给自己挖很多坑,但是不可控组件并不是不可掌控,就用一节课的时间,我们了解一下不可控组件的小技巧。
我们先来做一个最简单的不可用组件。
var MyForm = React.createClass({
render:function(){
return(
<div>
<input type="text"/>
</div>
)
}
});
组件完成之后给它加上一个onChange事件,发现是可以监控到变化值的。如果要获得iput中的value值,需先拿到其DOM节点,然后获取其value值。
var MyForm = React.createClass({
handleChange:function(){
var inputValue=ReactDOM.findDOMNode(this.refs.jspang).value;
console.log(inputValue);
},
render:function(){
return(
<div>
<input type="text" onChange={this.handleChange} ref="jspang"/>
</div>
)
}
});
当然我们也可以给input加入默认值,但是不是value了,而是defaultValue。
<input type="text" onChange={this.handleChange} ref="jspang" defaultValue='jspang'/>
打赏