- Published on
state 到底是同步还是异步的?
18 之前 命中批量更新 是异步 否则同步,18 之后 异步
理解 React state 原理
更新过程
确定优先级
- flushSync中的
setState
- 正常执行上下文
- setTimeout 、 Promise 中的
setState
更新视图
setState
会对 newState
和 oldState
进行浅比较 如果没有发生变化,则组件不更新
同步还是异步
const [count, setCount] = useState(0)
useEffect(() => {
setCount(1)
console.log(count) // 0
setCount(3)
console.log(count) // 0
}, [])
console.log(`render: ${count}`)
// render: 0
// render: 3
这说明 setState
是异步的。而且两次 setState
只触发了一次 render
同步代码里面会批量更新 batch update
const [count, setCount] = useState(0)
useEffect(() => {
setCount(1)
setTimeout(() => {
setCount(3)
})
}, [])
console.log(`render: ${count}`)
// render: 0
// render: 1
// render: 3
在 setTimeout 里,每次 setState
都会重新 render
流程
setState 会调用
dispathAction
,创建一个 update 对象放到 fiber 节点的 updateQueue 上,然后调度渲染react 会先从触发 update 的 fiber 往上找到根 fiber 节点,然后再调用函数进行渲染
而 setState 是同步还是异步,也就是在这一段控制的。因为直接从 setTimeout 执行的异步代码是没有设置
excutionContext
的,那就会走到 NoContext 的分支,会立刻渲染. 可以使用unstable_batchedUpdates
(这里源码设置了excutionContext
),用于在异步环境继续开启批量更新。等 setState 全部执行完之后再调用渲染,效果就是批量的 setState 了
如果在 react18,使用新的
createRoot
API,就算是在setTimeout
中也可以批量执行
总结
- setState 的同步异步,但这个不是 setTimeout、Promise 那种异步,只是指 setState 之后是否 state 马上变了,是否马上 render。
- React 的渲染流程,包括 render 阶段、commit 阶段,render 阶段是从 vdom 转 fiber,包含 schedule 和 reconcile,commit 阶段是把 fiber 更新到 dom。
- setState 会创建 update 对象挂到 fiber 对象上,然后调度重新渲染。
- 在 react18 里面,如果用 createRoot 的 api,就不会有这种问题了。
- setState 是同步还是异步这个问题等 react18 普及以后就不会再有了,因为所有的 setState 都是异步批量执行了。