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

dar*_*urf 112 javascript state setstate reactjs

我知道React可以异步和批量执行状态更新以进行性能优化.因此,在调用之后,您永远不能相信要更新的状态setState.但是你可以信任的反应更新相同的顺序状态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 });
  }
}
Run Code Online (Sandbox Code Playgroud)

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 });
  }
}
Run Code Online (Sandbox Code Playgroud)

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

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

Dan*_*mov 288

我在React工作.

TLDR:

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

  • 相同的组件?

是.

  • 不同的组件?

是.

订单的更新总是尊重.你是否看到它们之间的中间状态取决于你是否在一个批次内.

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

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


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

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

更新始终按其发生的顺序浅层合并.因此,如果第一次更新是{a: 10},第二次更新,{b: 20}第三次更新{a: 30},则呈现状态将是{a: 30, b: 20}.对同一状态键的更新更新(例如,a在我的示例中)总是"获胜".

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


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

但是,在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
});
Run Code Online (Sandbox Code Playgroud)

我们意识到行为不同取决于你是否在事件处理程序中是不方便.这将在未来的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
});
Run Code Online (Sandbox Code Playgroud)

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

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


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

  • 这个答案应该添加到reactjs.org文档中 (4认同)
  • 一种*“始终获得正确顺序”* 的方法是创建一个临时对象,分配不同的值(例如`obj.a = true; obj.b = true`),然后在最后执行`this.setState (对象)`。无论您是否在事件处理程序中,这都是安全的。如果您经常发现自己犯了在事件处理程序之外多次设置状态的错误,这可能是一个巧妙的技巧。 (2认同)
  • 您能否在这篇文章中澄清“React 事件处理程序”是否包括 `componentDidUpdate` 和 React 16 中的其他生命周期回调?提前致谢! (2认同)

Mos*_*ini 5

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

Object.assign(
  previousState,
  {quantity: state.quantity + 1},
  {quantity: state.quantity + 1},
  ...
)
Run Code Online (Sandbox Code Playgroud)

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


Mic*_*hal 5

这实际上是一个非常有趣的问题,但答案应该不会太复杂。在媒体上有一篇很棒的文章有答案。

1)如果这样做

this.setState({ a: true });
this.setState({ b: true });
Run Code Online (Sandbox Code Playgroud)

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

但是,如果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});
Run Code Online (Sandbox Code Playgroud)

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

这是在文章中提到的: 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}));
Run Code Online (Sandbox Code Playgroud)

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


Ali*_*Ali 5

文档中所示

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

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