React 表单介绍-包括受控组件和非受控组件

2021-06-10 0 By admin

在这里受控组件和非受控组件都是依托表单场景使用的;所以这篇的整理思路是通过学习表单组件的使用,来了解受控组件和非受控组件的特性。

一、表单和受控组件介绍

在 React 里,HTML 表单元素的工作方式和其他的 DOM 元素有些不同,这是因为表单元素通常会保持一些内部的 state。通过 state 可以很方便的处理表单的提交,同时还可以访问用户填写的表单数据。实现这种效果的标准方法是使用“受控组件”。

1.1、受控组件

在 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>
    );
  }
}

由于在表单元素上设置了 value 属性,因此显示的值将始终为 this.state.value,这使得 React 的 state 成为唯一数据源。由于 handlechange 在每次按键时都会执行并更新 React 的 state,因此显示的值将随着用户输入而更新。

对于受控组件来说,输入的值始终由 React 的 state 驱动。你也可以将 value 传递给其他 UI 元素,或者通过其他事件处理函数重置,但这意味着你需要编写更多的代码。

1.2、textarea 标签

在 React 中,textarea 使用 value 属性定义其文本内容。这样,可以使得使用 textarea 的表单和使用单行 input 的表单非常类似。

1.3、select 标签

在多选框中 React 并不会使用 selected 属性定义默认选择项,而是在根 select 标签上使用 value 属性实现。

1.4、处理多个输入

当需要处理多个 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>
    );
  }
}

1.5、受控组件的替代品

有时使用受控组件会很麻烦,因为你需要为数据变化的每种方式都编写事件处理函数,并通过一个 React 组件传递所有的输入 state。
当你将之前的代码库转换为 React 或将 React 应用程序与非 React 库集成时,这可能会令人厌烦。在这些情况下,你可能希望使用非受控组件, 这是实现输入表单的另一种方式。

二、表单和非受控组件

在大多数情况下,我们推荐使用 受控组件 来处理表单数据。在一个受控组件中,表单数据是由 React 组件来管理的。
另一种替代方案是使用非受控组件,这时表单数据将交由 DOM 节点来处理。
通过编写一个非受控组件,我们就免去为每个状态更新都编写数据处理函数的麻烦,而可以使用 ref 来从 DOM 节点中获取表单数据。
例如,下面的代码使用非受控组件接受一个表单的值:

class NameForm extends React.Component {
  constructor(props) {
    super(props);
    this.handleSubmit = this.handleSubmit.bind(this);
    this.input = React.createRef();
}
  handleSubmit(event) {
    alert('A name was submitted: ' + this.input.current.value);    event.preventDefault();
  }
  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          Name:
          <input type="text" ref={this.input} />        </label>
        <input type="submit" value="Submit" />
      </form>
    );
  }
}

因为非受控组件将真实数据储存在 DOM 节点中,所以在使用非受控组件时,有时候反而更容易同时集成 React 和非 React 代码。
如果你不介意代码美观性,并且希望快速编写代码,使用非受控组件往往可以减少你的代码量。否则,你应该使用受控组件。

2.1、默认值

在 React 渲染生命周期时,表单元素上的 value 将会覆盖 DOM 节点中的值。
在非受控组件中,你经常希望 React 能赋予组件一个初始值,但是不去控制后续的更新。在这种情况下, 你可以指定一个 defaultValue 属性,而不是 value。
在一个组件已经挂载之后去更新 defaultValue 属性的值,不会造成 DOM 上值的任何更新。

render() {
  return (
    <form onSubmit={this.handleSubmit}>
      <label>
        Name:
        <input
          defaultValue="Bob"
          type="text"
          ref={this.input} />
      </label>
      <input type="submit" value="Submit" />
    </form>
  );
}

同样,<input type=”checkbox”> 和 <input type=”radio”> 支持 defaultChecked,<select> 和 <textarea> 支持 defaultValue。