React 表单组件

之前写 AS_firedog 项目的时候,遇到一个问题,就是怎么获取到 <input />的值,最后虽然在网上找了份代码解决了,但是还是很懵懂。

在《深入 React 技术栈》中,我就领略到了 React 的表单组件和原生组件的不同,React 中很多很多组件都需要自己封装后才能实现原生的效果,而这带来的,是 React 可以对表单的状态进行控制,另外,在状态里已经可以拿到表单的值了,这一步在 html 中我们也是需要手动去处理的。

交互属性

表单组件支持几个受用户影响的属性:

  • value,用于<input><textarea>组件,获取值。
  • checked,用于类型为checkbox或者radio<input>组件
  • selected,用于<option>组件。

当上面的三个属性发生变化时,表单组件可以通过onChange回调函数来监听组件变化。

受控组件

React 受控组件更新 state 的流程

  1. 可以通过在初始 state 中设置表单的默认值。
  2. 每单表单的值发生变化时,调用onChange事件处理器。
  3. 事件处理器通过合成事件对象e(event) 来拿到改变后的状态,并更新应用的state
  4. setState触发 View 的重新渲染,完成表单组件值的更新。

值得注意的是,React 中我们不能想 HTML 一样把默认值设置在value中,这样子组件的值将永远不会改变。

虽然 React 表单组件比 HTML 元素的组件要复杂,每次状态改变都会执行上面这几步,可是这样让我们的表单的状态更可靠,更可控,因为这意味着我们在渲染之前,对表单的值进行清洗和校验。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
state = {
value: 'Hello!'
}
handleChange(event) {
this.setState({value: event.target.value});
}
render() {
return (
<input
type="text"
value={this.state.value}
onChange={this.handleChange}
/>
);
}

下面我们可以用受控组件来截取用户输入的前 140 个字符,并转换为大写:

1
2
3
4
5
handleChange(e){
this.setState({
value: e.target.value.substring(0,140).toUpperCase(),
});
}

受控组件不维持一个自己的内部状态;它单纯的基于 props 渲染。

不受控组件

有受控组件,自然也就有不受控组件了。受控组件通过初始 state 来设定value的默认值,靠onChange方法来监听,用setState更新组件。而不受控组件压根就不设置value属性,它自己管理自己的状态。可以通过onChange方法监听变化。

那问题来了,如果我想写不受控组件,又想设置默认值呢?

答案是可以的,可以使用 defaultValue 属性。

1
2
3
render() {
return <input type="text" defaultValue="Hello!" />;
}

在 React 中,非受控组件是一种反模式,它的值不受组件自身的 state 或 props 控制。通常,需要通过为其添加 ref prop 来访问渲染后的底层 DOM 元素。defaultValuedefaultChecked props 只能在内部渲染时被使用。通过 defaultValue 或者 defaultChecked 来设置表单的默认值,它仅会被渲染一次,在后续的渲染时并不起作用。下面对比以下两个示例。

textarea 使用

React 的<textarea>的值不像 HTML 一样写成子节点。

1
<textarea name="description" value="This is a description." />

当然也不是不能那样写,如果 非要 使用子节点,效果和使用 defaultValue 一样。

栗子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class EssayForm extends React.Component {
constructor(props) {
super(props);
this.state = {
value: 'Please write an essay about your favorite DOM element.'
};

this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}

handleChange(event) {
this.setState({value: event.target.value});
}

handleSubmit(event) {
alert('An essay was submitted: ' + this.state.value);
event.preventDefault();
}

render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<textarea value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
}

select 使用

1
2
3
4
5
6
<select>
<option value="grapefruit">Grapefruit</option>
<option value="lime">Lime</option>
<option selected value="coconut">Coconut</option>
<option value="mango">Mango</option>
</select>

如果是不受控组件,则使用 defaultValue

注意:给 value 属性传递一个数组,可以选中多个选项:<select multiple={true} value={['B', 'C']}>

栗子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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('Your favorite flavor is: ' + this.state.value);
event.preventDefault();
}

render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Pick your favorite La Croix flavor:
<select value={this.state.value} onChange={this.handleChange}>
<option value="grapefruit">Grapefruit</option>
<option value="lime">Lime</option>
<option value="coconut">Coconut</option>
<option value="mango">Mango</option>
</select>
</label>
<input type="submit" value="Submit" />
</form>
);
}
}

总之,<input type="text">, <textarea>, 和 <select> 都十分类似 - 他们都通过传入一个value属性来实现对组件的控制。

多个输入的解决方法

当你有处理多个受控的input元素时,你可以通过给每个元素添加一个name属性,来让处理函数根据 event.target.name的值来选择做什么。

栗子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
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>
Is going:
<input
name="isGoing"
type="checkbox"
checked={this.state.isGoing}
onChange={this.handleInputChange} />
</label>
<br />
<label>
Number of guests:
<input
name="numberOfGuests"
type="number"
value={this.state.numberOfGuests}
onChange={this.handleInputChange} />
</label>
</form>
);
}
}

本文代码来源 discountry
2017 年 07 月 28 日 00:18:30


React 表单组件
https://bubao.github.io/posts/b1420a79.html
作者
一念
发布于
2017年7月28日
许可协议