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

标签 javascript reactjs state setstate

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

  • 相同的组件?
  • 不同的组件?

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

    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 执行这些状态更新的定义明确的方式,仅此而已。

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

    最佳答案

    我在 React 上工作。
    TLDR:

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

    • the same component?

    是的。
    • different components?

    是的。
    订购 的更新始终受到尊重。您是否看到它们“之间”的中间状态取决于您是否在批处理中。
    在 React 17 及更早版本中,默认情况下,仅对 React 事件处理程序内部的更新进行批处理 .有一个不稳定的 API 可以在您需要的极少数情况下强制在事件处理程序之外进行批处理。
    从 React 18 开始,React batches all updates by default . 请注意,React 永远不会从两个不同的有意事件(如点击或打字)批量更新,因此,例如,两个不同的按钮点击永远不会被批量更新。在少数不需要批处理的情况下,您可以使用 flushSync .

    理解这一点的关键是 不管有多少setState()调用你在 React 事件处理程序中执行的组件数量,它们将在事件结束时仅产生一次重新渲染 .这对于大型应用程序的良好性能至关重要,因为如果 ChildParent每次通话setState()在处理点击事件时,您不想重新渲染 Child两次。
    在您的两个示例中,setState()调用发生在 React 事件处理程序中。因此,它们总是在事件结束时一起刷新(并且您看不到中间状态)。
    更新始终为 按它们出现的顺序浅合并 .所以如果第一次更新是{a: 10} ,第二个是{b: 20} ,第三个是{a: 30} ,渲染状态将为{a: 30, b: 20} .对同一状态键的最新更新(例如,在我的示例中,如 a)总是“获胜”。this.state当我们在批处理结束时重新渲染 UI 时,对象会更新。因此,如果您需要根据之前的状态更新状态(例如递增计数器),您应该使用功能 setState(fn)为您提供先前状态的版本,而不是从 this.state 读取.如果您对此原因感到好奇,我会深入解释它 in this comment .

    在您的示例中,我们不会看到“中间状态”,因为我们是 在 React 事件处理程序中 启用批处理的位置(因为当我们退出该事件时,React“知道”)。
    然而,在 React 17 和更早的版本中,在 React 事件处理程序之外默认没有批处理 .因此,如果在您的示例中我们有一个 AJAX 响应处理程序而不是 handleClick , 每个 setState()将在发生时立即处理。在这种情况下,是的,你 查看 React 17 及更早版本中的中间状态:
    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 18 中,这不再是必需的,但在此之前,有一个 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 是“不稳定的”,因为我们最终将在 18 之后的某个主要版本(19 或更高版本)中将其删除。如果您需要在 React 事件处理程序之外的某些情况下强制批处理,您可以安全地依赖它直到 React 18。使用 React 18,您可以删除它,因为它不再有任何效果。

    总而言之,这是一个令人困惑的话题,因为默认情况下 React 过去只批处理内部事件处理程序。但解决方案不是批量减少,而是默认批量增加。这就是我们在 React 18 中所做的。

    关于javascript - React 是否保持状态更新的顺序?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48563650/

    相关文章:

    javascript - 为什么这个对象 $window 是 Angular http Promise 对象

    html - 移动设备上的表单字段缩小得太小

    javascript - 在 Redux 状态下存储具有原型(prototype)函数的对象

    jquery - OS X Chrome 10.0.648.151 上的 HTML 历史状态问题

    javascript - ReactJS - 在箭头函数中设置状态

    javascript - 用 Flot 识别悬停点?

    javascript - 带有 Ratchet 的 PHP WebSockets - 示例不起作用

    javascript - 使用 bxSlider 显示/隐藏筛选 slider 项

    reactjs - 如何将对象存储在组件的状态中?

    javascript - 在 react.js 上下文中获取 html 元素的数据属性