React是否保留状态更新的顺序? [英] Does React keep the order for state updates?

查看:150
本文介绍了React是否保留状态更新的顺序?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I know that React may perform state updates asynchronously and in batch for performance optimization. Therefore you can never trust the state to be updated after having called setState. But can you trust React to update the state in the same order as setState is called for


  1. 相同的组件?

  2. 不同的组件?

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

Consider clicking the button in the following examples:

1。 a是否为false且b为真 for:

1. Is there ever a possibility that a is false and b is true for:

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执行这些状态更新的明确方式,没有别的。

Keep in mind that these are extreme simplifications of my use case. I realize that I can do this differently, e.g. updating both state params at the same time in example 1, as well as performing the second state update in a callback to the first state update in example 2. However, this is not my question, and I am only interested in if there is a well defined way that React performs these state updates, nothing else.

非常感谢文档备份的任何答案。

Any answer backed up by documentation is greatly appreciated.

推荐答案

我在React工作。

TLDR:


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

But can you trust React to update the state in the same order as setState is called for


  • 相同的组件?

是。



  • 不同的组件?

是。

始终遵守更新的订单。你是否看到他们之间的中间状态取决于你是否在一个批次内。

The order of updates is always respected. Whether you see an intermediate state "between" them or not depends on whether you're inside in a batch or not.

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

Currently (React 16 and earlier), only updates inside React event handlers are batched by default. There is an unstable API to force batching outside of event handlers for rare cases when you need it.

在将来的版本中(可能是React 17及更高版本),React将批量全部默认情况下更新,因此您不必考虑这一点。与往常一样,我们将在 React blog 和发行说明中公布对此的任何更改。

In future versions (probably React 17 and later), React will batch all updates by default so you won't have to think about this. As always, we will announce any changes about this on the React blog and in the release notes.

理解这一点的关键是无论多少 setState()调用您在React事件处理程序中执行的组件数量,它们将在事件结束时仅生成一个重新呈现。这对于大型应用程序的良好性能至关重要,因为如果 Child Parent 每次调用 setState( )处理点击事件时,你不想重新渲染 Child 两次。

The key to understanding this is that no matter how many setState() calls in how many components you do inside a React event handler, they will produce only a single re-render at the end of the event. This is crucial for good performance in large applications because if Child and Parent each call setState() when handling a click event, you don't want to re-render the Child twice.

在两个示例中, setState()调用都发生在React事件处理程序中。因此,它们总是在事件结束时被刷新(并且您没有看到中间状态)。

In both of your examples, setState() calls happen inside a React event handler. Therefore they are always flushed together at the end of the event (and you don't see the intermediate state).

更新始终浅层合并在他们出现的顺序。因此,如果第一次更新是 {a:10} ,则第二次更新是 {b:20} ,第三次更新是 {a:30} ,呈现状态将是 {a:30,b:20} 。对同一个状态键的更新更新(例如,在我的示例中类似 a )总是获胜。

The updates are always shallowly merged in the order they occur. So if the first update is {a: 10}, the second is {b: 20}, and the third is {a: 30}, the rendered state will be {a: 30, b: 20}. The more recent update to the same state key (e.g. like a in my example) always "wins".

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

The this.state object is updated when we re-render the UI at the end of the batch. So if you need to update state based on a previous state (such as incrementing a counter), you should use the functional setState(fn) version that gives you the previous state, instead of reading from this.state. If you're curious about the reasoning for this, I explained it in depth in this comment.

在您的示例中,我们不会看到中间状态,因为我们在React事件处理程序里面启用批处理(因为当我们退出该事件时React知道)。

In your example, we wouldn't see the "intermediate state" because we are inside a React event handler where batching is enabled (because React "knows" when we're exiting that event).

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

However, both in React 16 and earlier versions, there is yet no batching by default outside of React event handlers. So if in your example we had an AJAX response handler instead of handleClick, each setState() would be processed immediately as it happens. In this case, yes, you would see an intermediate state:

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

We realize it's inconvenient that the behavior is different depending on whether you're in an event handler or not. This will change in a future React version that will batch all updates by default (and provide an opt-in API to flush changes synchronously). Until we switch the default behavior (potentially in React 17), there is an API you can use to force batching:

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 调用时,刷新更新。

Internally React event handlers are all being wrapped in unstable_batchedUpdates which is why they're batched by default. Note that wrapping an update in unstable_batchedUpdates twice has no effect. The updates are flushed when we exit the outermost unstable_batchedUpdates call.

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

That API is "unstable" in the sense that we will remove it when batching is already enabled by default. However, we won't remove it in a minor version, so you can safely rely on it until React 17 if you need to force batching in some cases outside of React event handlers.

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

To sum up, this is a confusing topic because React only batches inside event handlers by default. This will change in future versions, and the behavior will be more straightforward then. But the solution is not to batch less, it's to batch more by default. That's what we're going to do.

这篇关于React是否保留状态更新的顺序?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆