最近在看 React 18 时,发现 18 已经默认将你的状态更新进行批处理了,不需要再像 16、17 内使用 unstable_batchedUpdates 这种 Hack Api 了~

什么是批处理?

批处理是将多个状态更新分组到单个 re-render 内,以获得更好的性能操作。

比如在同一个点击事件内更新两个 state,React 将会把它们分到一个 render 内重新渲染,看下方 demo 内的 console 可体现。

17 Batch Demo:批处理

在多个状态更新的时候,批处理可以避免不必要的重新渲染。然而在旧版本内何时批处理也并不完全一致,比如在 click 内去请求数据后再更新 state,此时 react 则不会批量更新,而是进行两次独立的更新

17 Batch Demo: 不批处理外部事件处理程序(注意 render 次数)

在 React 18 之前,React 只在事件处理程序期间批量更新。默认情况下,React 不会对 Promise、setTimeout、原生事件处理程序(native event handlers)或其他的React默认不进行批处理的事件进行批处理操作。

自动批处理

从 React 18 的 creatRoot 开始,所有的更新都将自动批处理,无论它们来自何处。这意味着 timeouts, promises, native event handlers,或者任何其他事件内的更新都将以 React 事件内的更新相同的方式进行批处理。我们希望这会减少渲染,获得更好的性能。

18 Auto Batch Demo: use creatRoot

注意!React 18 仍可使用 render(旧行为 render 的存在只是为了跟容易对两个版本进行生产实验),且保留旧行为,与全新的 createRoot 存在差异!

如何不进行批处理

通常,批处理是安全的,但某些代码可能依赖于在状态更改后立即从 Dom 中读取某些内容。对于这些用例,可以使用 React.flushSync() 退出批处理

import { flushSync } from 'react-dom'; // Note: react-dom, not react

function handleClick() {
  flushSync(() => {
    setCounter(c => c + 1);
  });
  // React has updated the DOM by now
  flushSync(() => {
    setFlag(f => !f);
  });
  // React has updated the DOM by now
}

unstable_batchedUpdates 相同的是 flushSync 内的更新也是批量,所以不要批处理的话需要考虑下逻辑的正确性