批量更新(Batching)
React 是怎么把多次 setState 合并成一次的?
什么是 Batching?
多次 setState 合并为一次重新渲染,减少渲染次数。
React 17 及之前
- 合成事件(如 onClick):自动批量
- 原生事件(setTimeout、Promise、addEventListener):不批量
// React 17
setTimeout(() => {
setCount(c => c + 1); // 触发重渲染
setFlag(f => !f); // 又触发重渲染
}, 1000);
两次 setState 会触发两次重渲染。
React 18:自动批量
所有更新都会自动批量,无论在什么地方:
- Promise.then
- setTimeout
- 原生事件
- React 事件
// React 18
setTimeout(() => {
setCount(c => c + 1);
setFlag(f => !f);
}, 1000);
// 只触发一次重渲染!
好处
- 减少渲染次数
- 状态一致性:避免中间"半成品"渲染
退出批量
极少数情况需要更新后立刻读取 DOM,用 flushSync:
import { flushSync } from 'react-dom';
function handleClick() {
flushSync(() => {
setCount(c => c + 1);
});
// 这里 DOM 已经更新了
}
警告:强制同步渲染,严重影响性能,慎用!
总结
| 版本 | 行为 |
|---|
| React 17 | 合成事件自动批量,原生事件不批量 |
| React 18+ | 所有更新自动批量 |
React 18 基本上帮你搞定了一切优化。