5月28日 03:31

React setState 是同步还是异步?原理是什么?

setState 并非真正“异步”——它是批量延迟执行。调用 setState 时,React 把更新对象推入当前 Fiber 节点的 updateQueue(环形链表),然后调度一次重新渲染,而不是立即修改 state 和触发 DOM 更新。等调度机制在下一个工作单元执行时,才遍历 updateQueue 计算新 state 并渲染。

批量更新的核心逻辑:同一个事件循环内的多次 setState,只会产生一次渲染调度。合并发生在遍历 updateQueue 阶段——React 依次执行每个 update 的计算函数,得到最终 state,再进入 render。

javascript
// 直接值更新:三次调用,updateQueue 里三个 update // 但都基于同一个闭包中的 count,最终 count = 0 + 1 = 1 setCount(count + 1); setCount(count + 1); setCount(count + 1); // 函数式更新:每个 update 拿到前一个 update 的结果 // count = ((0+1)+1)+1 = 3 setCount(c => c + 1); setCount(c => c + 1); setCount(c => c + 1);

追问

setState 是同步还是异步?

都不是。它本质是同步入队 + 延迟渲染。React 17 中,事件处理器外(setTimeout、原生事件)没有批量机制,setState 后能立即在同步代码中读到新值,看起来像“同步”——但这不是真正的同步,只是批量边界不同。React 18 用 createRoot 统一了所有场景的批处理。

React 18 自动批处理的原理是什么?

React 18 引入新调度入口 scheduleUpdateOnFiber,替代了原来的 enqueueSetState。无论更新来自事件处理器、setTimeout 还是 Promise,都走同一条调度路径。内部用 lane 模型(替代 expiration time)管理优先级,Scheduler 模块按优先级安排回调执行时机,实现所有场景统一批处理。

什么情况下 setState 后组件不会重新渲染?

bailout 机制:React 在渲染前会比较新旧 state(浅比较)。如果值没变,直接跳过该组件的渲染。常见陷阱——setState(obj) 传入同一个引用,浅比较相等,不会触发更新。必须创建新对象:setState({...obj, key: newVal})

函数式更新什么场景必须用?

依赖前一个 state 时必须用。典型场景:计数器、队列操作(往数组追加元素)。函数式更新能保证每次拿到的都是链表中上一个 update 计算后的最新值,而不是闭包捕获的旧值。

标签:React前端