了解ReactJS受控表单组件

时间:2017-03-24 23:33:00

标签: javascript reactjs

我正在根据以下页面实现以下代码:https://facebook.github.io/react/docs/forms.html

class Reservation extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      isGoing: true,
      numberOfGuests: 2
    };

    this.handleInputChange = this.handleInputChange.bind(this);
    this.handleSubmit = this.handleSubmit.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
    });
  }

  handleSubmit(event) {
    event.preventDefault();

    let data = {
      isGoing: this.state.isGoing,
      numberOfGuests: this.state.numberofGuests
    }

    /* Send data in ajax request here */

  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <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>
        <input type="submit" value="Submit" />
      </form>
    );
  }
}

我对此有一些疑问:

  1. 为什么我们需要将组件值存储在状态中?为什么不 只需抓住正常提交表单时我们需要的值 将使用标准JavaScript完成?推荐的方法似乎是为输入或删除的每个字符重新加载render函数。对我来说这没什么意义。
  2. 由于setState的执行是异步的,并且this.setState()之间可能存在延迟并实际使用this.state.numberOfGuests访问状态,这意味着此代码可能会在设置之前最终获取状态?如果是这样,为什么在官方的React文档中建议使用此代码?如果没有,为什么不呢?

3 个答案:

答案 0 :(得分:5)

关于第二点,那么在handleSubmit中的状态更新完成之前,handleInputChanged在逻辑上可能会运行。 React文档中没有提到这个原因,或者通常是任何人都关心的原因是因为setState函数很快就会运行真正。作为一项实验,我制作了a codepen to determine the average time taken for setState to run。它似乎需要约0.02毫秒的量级。没有人可以改变他们的输入,然后在不到那个时间内提交表单。事实上,e.preventDefault()中的handleSubmit电话无论如何都要花费近四分之一的时间。

如果您在继续之前完成setState绝对至关重要的情况,那么您可以使用setState的回调函数,例如

this.setState({
    colour: 'red'
}, () => {
    console.log(this.state.color)
});

然后red始终进行记录,而不是记录上一个值的以下内容。

this.setState({
    colour: 'red'
});
console.log(this.state.color);

答案 1 :(得分:3)

非常好的问题!以下是我对他们的看法:

<强> 1。受控制或不受控制 - 这是问题

您不必使用受控表单元素。您可以使用uncontrolled并按照您在onFormSubmit处理程序中的建议获取值,例如event.isGoing.value - 简单的JavaScript(或使用refs作为某些React文章建议)。您甚至可以通过使用defaultValue={myDefaultValue}来猜测,设置一个不受控制的默认值。

如上所述,使用受控组件的一个原因是,如果您在用户仍在键入时希望提供实时反馈。假设您需要执行自动完成查找或提供密码强度等验证。使用状态中的值重新呈现的受控组件使这非常简单。

<强> 2。 this.setState()异步问题?

[可能不正确],我在内部查看组件状态更新,就像队列系统一样。在处理同步代码时,对this.setState()的调用不会丢失,也不应覆盖另一个调用。但是,可能有一段时间渲染在setState更新后面运行,但它最终将具有并呈现最新值。例如:用户键入3个字符,但他们只看到2个字符,然后很短的时间后他们会看到第3个字符。因此,有一个时间点,this.state的读取读取了一个旧的&#34;价值,但它最终仍然更新。我希望我在这里有意义。

现在,我提到上面的同步代码,因为使用异步代码(比如使用AJAX),您可能会引入竞争条件,其中this.setState()会覆盖较新的状态值。

答案 2 :(得分:3)

为什么我们需要将组件值存储在状态?

我们并不是需要将组件值存储在状态中,并且在表单提交上访问这些值完全没问题。

但是,将组件值存储在状态中有其自身的优点。这背后的主要原因是将React状态作为单一事实来源。当状态更新时(在handleInputChange方法上),React会检查需要重新渲染的组件(或者特别是组件或子树的部分)。

使用这种方法,React帮助我们实现之前通过Two Way Binding helpers实现的目标。简而言之:

Form updated -> handleInputChange -> state updated -> updated state passed to other components accessing it

例如,您有一个<Card />组件需要用户通过<Form />组件输入,并且您希望在用户输入时显示信息,那么最好的方法是更新您的状态会导致React查找访问它的子树并仅重新渲染它们。因此,当您输入<Card />时,您的<Form />组件会自动更新。

此外,React不会为每个键入的单个字符重新呈现所有内容,而只会重新呈现需要反映文本更改的子树。请参阅React diffing algorithm了解具体方法。在这种情况下,当您在表单中的特定字段中键入字符时,只会重新呈现组件中显示该字段的那些子树。

执行setState和异步行为

正如React docs所述:

  

州更新可能是异步的

     

React可以将多个setState()个调用批处理为一个更新   性能

     

因为this.propsthis.state可以异步更新,所以   应该依赖于计算下一个状态的值。

您的代码应该可以正常工作,因为您不依赖于先前的状态来计算下一个状态,而且您还在另一个块中访问this.state。所以你的状态更新应该反映得很好;但是,如果您不想考虑状态更新(或怀疑您的代码可能会提升以前的状态),React文档还提到了一种替代方法(实际上更好)接受function而不是object

this.setState((prevState, props) => ({
  counter: prevState.counter + props.increment
}));

如果您仍然想知道何时可以安全地使用setState方法,请在其他组件依赖的情况下使用它,或者当您不需要< strong> persist 状态(保存在本地存储或服务器中)。在处理大型项目时,最好使用状态容器来避免麻烦,例如Redux