javascript - React是否保留状态更新的顺序?

我知道React可以异步和批量执行状态更新以进行性能优化。 因此,在调用setState之后,您永远不能相信状态会更新。但是您能否相信React以与调用setState相同的顺序更新状态

  1. 相同的组件?
  2. 不同的组件?

考虑单击以下示例中的按钮:

1.是否有可能a是假的,b对于:

class Container extends React.Component {
  constructor(props) {
    super(props);
    this.state = { a: false, b: false };
  }

  render() {
    return <Button onClick={this.handleClick}/>
  }

  handleClick = () => {
    this.setState({ a: true });
    this.setState({ b: true });
  }
}

2.是否有可能a是假的,b对于:

class SuperContainer extends React.Component {
  constructor(props) {
    super(props);
    this.state = { a: false };
  }

  render() {
    return <Container setParentState={this.setState.bind(this)}/>
  }
}

class Container extends React.Component {
  constructor(props) {
    super(props);
    this.state = { b: false };
  }

  render() {
    return <Button onClick={this.handleClick}/>
  }

  handleClick = () => {
    this.props.setParentState({ a: true });
    this.setState({ b: true });
  }
}

请记住,这些是我的用例的极端简化。 我意识到我可以这样做,例如 在示例1中同时更新两个状态参数,以及在示例2中的第一个状态更新的回调中执行第二个状态更新。但是,这不是我的问题,我只关心是否存在 React执行这些状态更新的明确方式,没有别的。

任何由文档支持的答案都非常感谢。

darksmurf asked 2019-08-19T17:20:31Z
4个解决方案
250 votes

我在React工作。

TLDR:

但是你能相信React以与调用setState相同的顺序更新状态

  • 相同的组件?

是。

  • 不同的组件?

是。

始终遵守更新顺序。 你是否看到了一个中间状态&#34;之间的#34; 它们与否取决于您是否批量生产。

目前(React 16及更早版本),默认情况下仅对React事件处理程序内的更新进行批处理。 在您需要时,有一个不稳定的API可以强制在事件处理程序之外进行批处理。

在未来的版本(可能是React 17及更高版本)中,React默认会批量处理所有更新,因此您不必考虑这一点。 与往常一样,我们将在React博客和发行说明中公布有关此内容的任何更改。


理解这一点的关键在于,无论您在React事件处理程序中执行了多少组件调用,它们将在事件结束时仅生成一个重新呈现。 这对于大型应用程序的良好性能至关重要,因为如果在处理点击事件时unstable_batchedUpdatesunstable_batchedUpdates各自调用unstable_batchedUpdates,则您不想重新渲染{a: 30, b: 20}两次。

在两个示例中,unstable_batchedUpdates调用都发生在React事件处理程序中。 因此,它们总是在事件结束时被冲洗在一起(并且你不会看到中间状态)。

更新始终按其发生的顺序浅层合并。 因此,如果第一次更新是unstable_batchedUpdates,第二次更新是unstable_batchedUpdates,第三次更新是unstable_batchedUpdates,渲染状态将是{a: 30, b: 20}。对同一状态键的更新更新(例如,像我的示例中的a)总是&#34;赢得&#3434;.

当我们在批处理结束时重新呈现UI时,会更新unstable_batchedUpdates对象。 因此,如果您需要根据以前的状态更新状态(例如递增计数器),则应使用功能unstable_batchedUpdates版本,该版本为您提供之前的状态,而不是从unstable_batchedUpdates读取。如果您对推理感到好奇 为此,我在这篇评论中进行了深入解释。


在你的例子中,我们不会看到&#34;中间状态&#34; 因为我们在React事件处理程序中,启用了批处理(因为React&#34;知道&#34;当我们退出该事件时)。

但是,在React 16和早期版本中,默认情况下,React事件处理程序之外没有默认批处理。 因此,如果在您的示例中我们有一个AJAX响应处理程序而不是unstable_batchedUpdates,则会立即处理每个unstable_batchedUpdates。 在这种情况下,是的,你会看到一个中间状态:

promise.then(() => {
  // We're not in an event handler, so these are flushed separately.
  this.setState({a: true}); // Re-renders with {a: true, b: false }
  this.setState({b: true}); // Re-renders with {a: true, b: true }
  this.props.setParentState(); // Re-renders the parent
});

我们意识到,根据您是否在事件处理程序中,行为会有所不同。 这将在未来的React版本中更改,默认情况下将批量处理所有更新(并提供一个选择性API以同步刷新更改)。 在我们切换默认行为(可能在React 17中)之前,您可以使用一个API来强制批处理:

promise.then(() => {
  // Forces batching
  ReactDOM.unstable_batchedUpdates(() => {
    this.setState({a: true}); // Doesn't re-render yet
    this.setState({b: true}); // Doesn't re-render yet
    this.props.setParentState(); // Doesn't re-render yet
  });
  // When we exit unstable_batchedUpdates, re-renders once
});

内部React事件处理程序都包含在unstable_batchedUpdates中,这就是他们默认批处理的原因。 请注意,将更新包装在unstable_batchedUpdates中两次无效。 当我们退出最外面的unstable_batchedUpdates呼叫时,刷新更新。

该API不稳定&#34; 在默认情况下已经启用批处理时我们将删除它。 但是,我们不会在次要版本中删除它,因此如果您需要在React事件处理程序之外的某些情况下强制批处理,则可以安全地依赖它直到React 17。


总而言之,这是一个令人困惑的主题,因为React默认只在事件处理程序内部进行批处理。 这将在未来版本中发生变化,然后行为将变得更加直截了当。 但是解决方案不是批量减少,而是默认批量更多。 这就是我们要做的事情。

Dan Abramov answered 2019-08-19T17:22:41Z
3 votes

这实际上是一个非常有趣的问题,但答案不应该太复杂。 媒体上有这篇伟大的文章可以回答这个问题。

1)如果你这样做

this.setState({ a: true });
this.setState({ b: true });

我不认为this.state.value === 3将是a,而b将因批量而成为false

但是,如果this.state.value === 3依赖于a,那么确实可能存在您无法获得预期状态的情况。

// assuming this.state = { value: 0 };
this.setState({ value: this.state.value + 1});
this.setState({ value: this.state.value + 1});
this.setState({ value: this.state.value + 1});

处理完所有上述电话后,this.state.value === 3将为1,而不是您所期望的3。

这在文章中提到:this.state.value === 3

// assuming this.state = { value: 0 };
this.setState((state) => ({ value: state.value + 1}));
this.setState((state) => ({ value: state.value + 1}));
this.setState((state) => ({ value: state.value + 1}));

这将给我们this.state.value === 3

Michal answered 2019-08-19T17:23:52Z
3 votes

在文档中

setState()将对组件状态的更改排入队列并告知React   这个组件及其子组件需要重新呈现   更新状态。 这是用于更新用户的主要方法   接口响应事件处理程序和服务器响应。

它将执行队列中的更改(FIFO:先入先出),第一次调用将首先执行

Ali answered 2019-08-19T17:24:34Z
2 votes

在同一周期内的多个呼叫可以一起批量处理。 例如,如果您尝试在同一周期中多次增加项目数量,则会产生相当于:

Object.assign(
  previousState,
  {quantity: state.quantity + 1},
  {quantity: state.quantity + 1},
  ...
)

[https://reactjs.org/docs/react-component.html]

Mosè Raguzzini answered 2019-08-19T17:25:08Z
translate from https://stackoverflow.com:/questions/48563650/does-react-keep-the-order-for-state-updates