9

九、表单处理

HTML表单元素与React中的其他DOM元素有点不同,因为表单元素自然地保留一些内部状态。 例如,这种采用纯HTML格式的表单接受单个name:

<form>
    <label>
        Name:
        <input type="text" name="name" />
    </label>
    <input type="submit" value="Submit" />
</form>

此表单具有在用户提交表单时浏览到新页面的默认HTML表单行为。
如果你想在React中这个行为,它是可以工作的。 但在大多数情况下,使用JavaScript函数处理表单的提交并访问用户在表单中输入的数据很方便。 实现这一点的标准方法是使用称为“可控组件”的技术。

可控组件

在HTML中,诸如<input><textarea><select>的表单元素通常保持它们自己的状态并且是基于用户输入来更新它的。 在React中,可变state通常保存在组件的state属性中,并且仅使用setState()更新。

我们可以通过使React state是“真理的唯一来源”来结合这两者。 后续用户在该表单进行输入时依然受React组件控制。 其值由React以这种方式控制的可输入表单元素被称为“可控组件”

例如,如果我们想让前面的示例日志在提交时记录下名称,我们可以将表单作为可控组件

import React from 'react';
import ReactDOM from 'react-dom';

class NameForm extends React.Component {
    constructor(props) {
        super(props);
        this.state = {value: ''};
        this.change = this.change.bind(this);
        this.submit = this.submit.bind(this);
    }

    change(e) {
        this.setState({value: e.target.value});
    }

    submit(e) {
        e.preventDefault();
        console.log('A name was submitted: ', this.state.value);
    }

    render() {
        return (
            <form onSubmit={this.submit} >
                <label>
                    Name:
                    <input type="text"
                           value={this.state.value}
                           onChange={this.change}
                    />
                </label>
                <input type="submit" value="Submit" />
            </form>
        )
    };
}
ReactDOM.render(
    <NameForm/>,
    document.getElementById('root')
);

因为value属性是在form元素上设置的,所以显示的值将总是为this.state.value,让React state成其value的为真正来源。 由于change在每次击键时运行并更新React state,因此显示的值将随用户键入而更新。

使用受控组件,使得直接修改或验证用户输入的时候,每个state变量都将具有关联的处理函数。例如,如果我们想强制执行name全部用大写字母写,我们可以写change为:

change(e) {
    this.setState({value: e.target.value.toUpperCase()});
}

textarea标签

在HTML中,<textarea>元素的子元素就是它的文本内容:

<textarea>
    Hello there, my name is zhangyatao!
</textarea>

在React中,<textarea>改为使用value属性。 这样,使用<textarea>的表单可以非常类似于使用单行输入的表单:

class EssayForm extends React.Component {
    constructor(props) {
        super(props);
        this.state = {value: '请填写一个你喜欢的dom元素'};
        this.change = this.change.bind(this);
        this.submit = this.submit.bind(this);
    }
    change(e) {
        this.setState({value: e.target.value});
    }
    submit(e) {
        e.preventDefault();
        console.log('有东西被提交了:', this.state.value);
    }
    render() {
        return (
            <form onSubmit={this.submit}>
                <label>
                    Name:
                    <textarea value={this.state.value} onChange={this.change} />
                </label>
                <input type="submit" value="Submit" />
            </form>
        );
    }
}

请注意,this.state.value在构造函数中初始化,因此textarea默认显示其中的文本。

select标签

在HTML中,<select>创建一个下拉列表。 例如,此HTML创建一个下拉列表:

<select>
    <option value="fruit">fruit</option>
    <option value="lime">lime</option>
    <option selected value="coconut">coconut</option>
    <option value="mango">mango</option>
</select>

请注意,由于selected属性coconut选项默认被选中的。
在React中在一般是在根select标签上使用value属性而不是使用selected属性。 这在受控组件中很方便,因为您只需要在一个地方更新它。 例如:

import React from 'react';
import ReactDOM from 'react-dom';

class FlavorForm extends React.Component {
    constructor(props) {
        super(props);
        this.state = {value: 'coconut'};
        this.change = this.change.bind(this);
        this.submit = this.submit.bind(this);
    }

    change(e) {
        this.setState({value: e.target.value});
    }

    submit(e) {
        e.preventDefault();
        console.log('你喜欢的是:', this.state.value);
    }

    render() {
        return (
            <form onSubmit={this.submit}>
                <label>
                    请选择一个你喜欢的水果
                    <select value={this.state.value} onChange={this.change}>
                        <option value="fruit">fruit</option>
                        <option value="lime">lime</option>
                        <option value="coconut">coconut</option>
                        <option value="mango">mango</option>
                    </select>
                </label>
                <input type="submit" value="submit"/>
            </form>
        );
    }
}
ReactDOM.render(
    <FlavorForm/>,
    document.getElementById('root')
);

总的来说,这使得<input type =“text”><textarea><select>都非常类似 - 它们都接受一个value属性,您可以使用它来实现可控组件

可控组件的备选方案

使用受控组件有时可能很乏味,因为您每次都需要为数据更改去编写事件处理程序,并通过React组件管理所有输入状态。
当您将已经存在的项目转换为React或将React应用程序与非React库集成时,这可能会变得特别烦人。
在这些情况下,您可能想要选择那些不可控组件(后续会有详细讲解),一种用于实现输入表单的替代技术。


张亚涛
5.3k 声望2.8k 粉丝

人首先应该接受现实,承认问题的存在,并且反思它。而不是首先就跳出来回避这个问题,找比我们更差的。质疑甚至抨击提出问题的人,并且一杆子打死。