React 是否保持 state 更新的顺序

React状态更新是异步的,为了性能优化,状态更新都是批量更新的。
但是否可以确认 setState 调用后状态的更新顺序呢?
考虑以下按钮点击的例子:
是否有可能 a 是 false,b 是 true?

import React, { PureComponent } from 'react';

class Subclass extends PureComponent {
    state = {
        a: true,
        b: true
    }

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

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

是否有可能 a 是 false,b 是 true?

class Parent extends PureComponent {
    state = {
        a: false
    }

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

class Sub extends PureComponent {
    state = {
        b: false
    }

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

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

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

Dan Abramov 先给出了结论:

同一个组件中,setState 是否有确定的顺序?是的。
不同组件中,setState 是否有确定的顺序?是的。

状态始终是按照特定的顺序更新的。无论你是否看到介于两个状态之间的一个中间状态,无论你是否在批处理内。
目前(React 16 及更早版本),默认情况下,只有 React 事件处理程序中的更新才会被批处理。有一个不稳定(unstable)的 API 来强制在事件处理程序之外进行批处理,以便在需要时处理罕见的情况。
在未来的版本(可能是 React 17 或更高版本)中,React 将 默认批量更新所有更新
在 React 事件处理程序中,不论 setState() 调用了多少次,也不论 setState()被多少个组件调用,它们在事件结束时只会生成一次重新渲染
在这两个例子中,setState() 调用都发生在 React 事件处理程序中。因此,在事件结束时,他们总是被合并到一起(而且你看不到中间状态)
更新总是 按照它们发生的顺序进行浅合并(shallowly merge)
在 React 16 和更早版本中,React 事件处理程序之外还没有默认的批处理。因此,如果在例子中,我们把 handleClick 替换为 AJAX 处理程序,那么每个 setState() 都会立即处理。在这种情况下,你会看到一个中间状态:

promise.then(() => {
  // 我们不在事件处理程序中,因此它们都会被刷新。
  this.setState({a: true}); // 使用 {a: true, b: false } 重新渲染
  this.setState({b: true}); // 使用 {a: true, b: true } 重新渲染
  this.props.setParentState(); // 重新渲染父组件
});

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

promise.then(() => {
  // 强制批量处理
  ReactDOM.unstable_batchedUpdates(() => {
    this.setState({a: true}); // 不重新渲染
    this.setState({b: true}); // 不重新渲染
    this.props.setParentState(); // 不重新渲染
  });
  // 当我们退出 unstable_batchedUpdates函数后,重新渲染一次
});

在 React 内部,事件处理程序都被包装在 unstable_batchedUpdates 内,这就是默认情况下批处理的原因。请注意,将 setState 封装 unstable_batchedUpdates 两次是不起作用的。当我们退出最外层的unstable_batchedUpdates 调用时,更新被刷新。
原文: React 是否保持 state 更新的顺序?

posted @ 2018-02-06 10:38  joe_ice  阅读(1183)  评论(0编辑  收藏  举报