React 批处理机制

React 批处理机制

我们开发过程中发现,调用 setState,然后获取 state 时候,发现不是最新的值,这是为什么呢?这就和 React 批处理机制有关系。
一次性调用好几次 setState,肯定不会调用一次组件就更新渲染一次,这样太消耗性能了。
react 做法,每次调用都记录下来,然后合并得到最新的值,最后才执行更新。
一般来说生命周期、react 事件里面都会命中批处理机制。
批处理的流程如下:

批处理机制

setState 本身并不是异步的,只不过批处理的机制,给人一种异步的假象。

分析一下整体流程:

● 当我们触发更新时候 setState
● 页面不会立即更新,而是把 state 存入 pendingState 队列中,要更新的组件存入 dirtyComponent 队列中。
● 是否开始更新,有个开关控制的 isBatchingUpdate,它是全局变量,如果为 false 才会进行更新。

什么时候为 false?
● 变量初始化时候(页面第一次渲染的时候)。
● 上一次更新执行完毕的时候。
● 为了 false 了,就把 dirtyComponent 中组件和 pendingState 的 state 进行更新,此时又把 isBatchingUpdate 设置为 true,这样就可以确保组件不会被重新渲染多次。

所以说下面的代码不会打印出最新的值,因为 state 都被缓存到 pendingState 里面了,要等批处理机制完成后才会统一更新的。

1
2
this.setState({ name: "chenjiang" });
console.log(this.state.name);

为什么定时器里面和原生事件里面可以获取最新的值?

结论:异步代码,事件循环导致。

上面提到了有个全局变量 isBatchingUpdate,默认是 false,更新开始设置为 true,更新结束为 false。

1
2
3
4
5
6
7
8
// 假设点击按钮,执行了下面的代码。
setTimeout(() => {
console.log("调用 setState");
this.setState({
index: this.state.index + 1,
});
console.log("state", this.state.index);
}, 0);

分析一下上面的代码,为什么可以获取最新的值。

  1. 一开始 isBatchingUpdate 为 false。
  2. 点击按钮执行了定时代码,由于 setTimeout 是异步代码,回调函数不会立即执行,要等同步代码执行完毕,进行事件循环的时候才会执行。
  3. 代码走完了,没有更新操作,isBatchingUpdate 也就不会设置为 true。
  4. 等到事件循环,执行定时器的回调函数,但是此时 isBatchingUpdate 是 false,就直接对 pendingState 和 dirtyComponent 进行更新,所以下面的 log 可以获取到最新的数据。(从上面流程图可以看出)