1.表单
在 React 里,HTML 表单元素的工作方式和其他的 DOM 元素有些不同,这是因为表单元素通常会保持一些内部的 state。例如这个纯 HTML 表单只接受一个名称:
<form>
<label>
名字:
<input type="text" name="name" />
</label>
<input type="submit" value="提交" />
</form>
此表单具有默认的 HTML 表单行为,即在用户提交表单后浏览到新页面。如果你在 React 中执行相同的代码,它依然有效。但大多数情况下,使用 JavaScript 函数可以很方便的处理表单的提交, 同时还可以访问用户填写的表单数据。实现这种效果的标准方式是使用“受控组件”。
2.受控组件
在 HTML 中,表单元素(如
<input>
、<textarea>
和<select>
)之类的表单元素通常自己维护 state,并根据用户输入进行更新。而在 React 中,可变状态(mutable state)通常保存在组件的 state 属性中,并且只能通过使用setState()
来更新。
我们可以把两者结合起来,使 React 的 state 成为“唯一数据源”。渲染表单的 React 组件还控制着用户输入过程中表单发生的操作。被 React 以这种方式控制取值的表单输入元素就叫做“受控组件”。
class NameForm extends React.Component {
constructor(props) {
super(props);
this.state = {value: ''};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('提交的名字: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
名字:
<input type="text" value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="提交" />
</form>
);
}
}
3.textarea 标签
在 HTML 中, 元素通过其子元素定义其文本:
<textarea>
你好, 这是在 text area 里的文本
</textarea>
而在 React 中,
<textarea>
使用value
属性代替。这样,可以使得使用<textarea>
的表单和使用单行 input 的表单非常类似:
class EssayForm extends React.Component {
constructor(props) {
super(props);
this.state = {
value: '请撰写一篇关于你喜欢的 DOM 元素的文章.'
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('提交的文章: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
文章:
<textarea value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="提交" />
</form>
);
}
}
请注意,
this.state.value
初始化于构造函数中,因此文本区域默认有初值。
4.select 标签
在 HTML 中,
<select>
创建下拉列表标签。例如,如下 HTML 创建了水果相关的下拉列表:
<select>
<option value="grapefruit">葡萄柚</option>
<option value="lime">酸橙</option>
<option selected value="coconut">椰子</option>
<option value="mango">芒果</option>
</select>
请注意,由于
selected
属性的缘故,椰子选项默认被选中。React 并不会使用selected
属性,而是在根select
标签上使用value
属性。这在受控组件中更便捷,因为您只需要在根标签中更新它。
class FlavorForm extends React.Component {
constructor(props) {
super(props);
this.state = {value: 'coconut'};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('你喜欢的风味是: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
选择你喜欢的风味:
<select value={this.state.value} onChange={this.handleChange}>
<option value="grapefruit">葡萄柚</option>
<option value="lime">酸橙</option>
<option value="coconut">椰子</option>
<option value="mango">芒果</option>
</select>
</label>
<input type="submit" value="提交" />
</form>
);
}
}
总的来说,这使得
<input type="text">
,<textarea>
和<select>
之类的标签都非常相似—它们都接受一个value
属性,你可以使用它来实现受控组件。
5.文件 input 标签
在 HTML 中,
<input type=“file”>
允许用户从存储设备中选择一个或多个文件,将其上传到服务器,或通过使用 JavaScript 的File API
进行控制。因为它的value
只读,所以它是 React 中的一个非受控组件。
<input type="file" />
input的双向数据绑定
- 将数据模型中的数据展示在表单控件内
- 当表单控件发生更改的时候,修改数据模型中的数据
6.处理多个输入
当需要处理多个
input
元素时,我们可以给每个元素添加name
属性,并让处理函数根据event.target.name
的值选择要执行的操作。
class Reservation extends React.Component {
constructor(props) {
super(props);
this.state = {
isGoing: true,
numberOfGuests: 2
};
this.handleInputChange = this.handleInputChange.bind(this);
}
handleInputChange(event) {
const target = event.target;
const value = target.type === 'checkbox' ? target.checked : target.value;
const name = target.name;
this.setState({
[name]: value
});
}
render() {
return (
<form>
<label>
参与:
<input
name="isGoing"
type="checkbox"
checked={this.state.isGoing}
onChange={this.handleInputChange} />
</label>
<br />
<label>
来宾人数:
<input
name="numberOfGuests"
type="number"
value={this.state.numberOfGuests}
onChange={this.handleInputChange} />
</label>
</form>
);
}
}
7.受控输入空值
在受控组件上指定
value
的prop
可以防止用户更改输入。如果指定了value
,但输入仍可编辑,则可能是意外地将value
设置为undefined
或null
。
ReactDOM.render(<input value="hi" />, mountNode);
setTimeout(function() {
ReactDOM.render(<input value={null} />, mountNode);
}, 1000);
8.综合示例
//多种表单控件的双向数据绑定
import React,{Component} from 'react';
class MyTest extends Component{
constructor(props) {
super(props);
this.state = {
genders:[{name:'男',value:'man'},{name:'女',value:'woman'}],
addresses:[{name:'上海',value:'Shanghai'},{name:'北京',value:'Beijin'}],
hobby:[{name:'游泳',value:'swimming'},{name:'跳舞',value:'dancing'},{ name:'唱歌',value:'singing'}],
form:{username:'',password:'',age:'',gender:'',hobbies:[],address:'',eva:''},
msg:'个人简历'
};
}
//事件处理程序
inputChange = (attr,e)=>{
this.setState({
form:{...this.state.form,[attr]:e.target.value}
});
}
checkboxChange = (e)=>{
//如果数组中没有它的值就添加,如果有就删除
let value = e.target.value;
let hobbies = [...this.state.form.hobbies];
if(hobbies.includes(value)){
//移除 找到当前元素索引然后通过索引移除
hobbies.filter((item)=>{
return item != value;
});
}else{
//添加
hobbies = hobbies.concat(value);
}
this.setState({
form:{...this.state.form,hobbies:hobbies}
},()=>{
console.log(this.state.form.hobbies);
})
}
btnSubmit = ()=>{
let obj = {...this.state.form};
console.log(obj);
}
render(){
const {form} = this.state;
return (
<div style={{padding:20}}>
<h1>{this.state.msg}</h1>
<form>
用户名:
<input value={form.username} type='text' onChange={this.inputChange.bind(this,'username')}/>
<br />
密码:
<input value={form.password} type='password' onChange={this.inputChange.bind(this,'password')}/>
<br />
年龄:
<input value={form.age} type='text' onChange={this.inputChange.bind(this,'age')}/>
<br />
性别:
{
this.state.genders.map((item,index)=>{
return (
<label key={index}>
<input type='radio' value={item.value} checked={form.gender===item.value?true:false} onChange={this.inputChange.bind(this,'gender')}/>{item.name}
</label>
);
})
}
<br />
爱好:
{
this.state.hobby.map((item, index) =>{
return (
<label htmlFor={item.value} key={index}>
<input id={item.value} type='checkbox' value={item.value} checked={form.hobbies.includes(item.value)} onChange={this.checkboxChange}/>{item.name}
</label>
);
})
}
<br />
地址:
<select value={form.address} onChange={this.inputChange.bind(this,'address')}>
<option value=''>请选择</option>
{
this.state.addresses.map((item,index)=>{
return <option key={index} value={item.valeu}>{item.name}</option>
})
}
</select>
<br />
自我评价:
<br />
<textarea value={form.eva} cols="30" rows="10" onChange={this.inputChange.bind(this,'eva')}></textarea>
<br />
</form>
<button onClick={this.btnSubmit} style={{width:100}}>提交</button>
</div>
);
}
}
export default MyTest;