Javascript React 是否保持状态更新的顺序?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/48563650/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 04:10:01  来源:igfitidea点击:

Does React keep the order for state updates?

javascriptreactjsstatesetstate

提问by darksmurf

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 setStateis calledfor

我知道 React 可能会异步和批量执行状态更新以进行性能优化。因此,您永远不能相信在调用setState. 但是你可以信任的反应更新相同的顺序状态setState被称为

  1. the same component?
  2. different components?
  1. 相同的组件?
  2. 不同的组件?

Consider clicking the button in the following examples:

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

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

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.Is there ever a possibility that a is false and b is truefor:

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

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.

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

Any answer backed up by documentation is greatly appreciated.

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

回答by Dan Abramov

I work on React.

我在 React 工作。

TLDR:

域名注册地址:

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

  • the same component?

但是你能相信 React 会按照调用 setState 的顺序更新状态吗?

  • 相同的组件?

Yes.

是的。

  • different components?
  • 不同的组件?

Yes.

是的。

The orderof 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.

订单的更新总是尊重。您是否看到它们“之间”的中间状态取决于您是否在批处理中。

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 16 及更早版本),默认情况下仅对 React 事件处理程序内的更新进行批处理。有一个不稳定的 API 可以在您需要时在极少数情况下强制在事件处理程序之外进行批处理。

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 blogand in the release notes.

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



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 Childand Parenteach call setState()when handling a click event, you don't want to re-render the Childtwice.

理解这一点的关键是,无论setState()在 React 事件处理程序中对多少个组件进行了多少次调用,它们都只会在事件结束时产生一次重新渲染。这对于大型应用程序中的良好性能至关重要,因为在处理单击事件时,如果ChildParent每次调用setState(),您都不想重新渲染Child两次。

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).

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

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 ain my example) always "wins".

更新总是按照它们发生的顺序浅合并。所以如果第一次更新是{a: 10},第二次是{b: 20},第三次是{a: 30},渲染状态将是{a: 30, b: 20}。对相同状态密钥的更新(例如a在我的示例中)总是“获胜”。

The this.stateobject 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.

this.state当我们在批处理结束时重新渲染 UI 时,对象会更新。因此,如果您需要根据先前状态更新状态(例如增加计数器),则应使用setState(fn)提供先前状态的功能版本,而不是从this.state. 如果您对此原因感到好奇,我在此评论中对其进行了深入解释。



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

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

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 wouldsee an intermediate state:

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

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

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:

我们意识到行为会因您是否在事件处理程序中而有所不同,这很不方便。这将在未来的 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
});

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

内部 React 事件处理程序都被包装在unstable_batchedUpdates其中,这就是默认情况下它们被批处理的原因。请注意,将更新包装unstable_batchedUpdates两次无效。当我们退出最外层unstable_batchedUpdates调用时,更新会被刷新。

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.

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



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 moreby default. That's what we're going to do.

总而言之,这是一个令人困惑的话题,因为 React 默认只在事件处理程序中进行批处理。这将在未来版本中改变,届时行为将更加直接。但解决方案不是批量减少,而是默认批量增加。这就是我们要做的。

回答by Michal

This is actually a quite interesting question but the answer shouldn't be too complicated. There is this great article on mediumthat has an answer.

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

1) If you do this

1)如果你这样做

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

I don't think that there will be a situation where awill be trueand bwill be falsebecause of batching.

我不认为会出现的情况下atruebfalse因为配料

However, if bis dependent on athen there indeed might be a situation where you wouldn't get the expected state.

但是,如果b依赖于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});

After all the above calls are processed this.state.valuewill be 1, not 3 like you would expect.

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

This is mentioned in the article: setState accepts a function as its parameter

这在文章中提到: setState accepts a function as its parameter

// 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 will give us this.state.value === 3

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

回答by Ali

as in doc

就像在文档中一样

setState() enqueueschanges to the component state and tells React that this component and its children need to be re-rendered with the updated state. This is the primary method you use to update the user interface in response to event handlers and server responses.

setState()组件状态的更改加入队列,并告诉 React 该组件及其子组件需要使用更新后的状态重新渲染。这是您用来更新用户界面以响应事件处理程序和服务器响应的主要方法。

it will preform the change as in queue (FIFO: First In First Out) the first call will be first to preform

它将按照队列(FIFO:先进先出)执行更改,第一个调用将首先执行

回答by Mosè Raguzzini

Multiple calls during the same cycle may be batched together. For example, if you attempt to increment an item quantity more than once in the same cycle, that will result in the equivalent of:

同一周期内的多个调用可以一起批处理。例如,如果您尝试在同一个周期内多次增加一个项目数量,将导致相当于:

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

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

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