看一下setState原理,state批处理,为什么同步下 state会合并处理,为什么setState(v=>v)可以拿到最新的state
先看下这段代码,都知道,会分为两次render,那么想一下,为什么
function App() {
const [count,setCount] = useState(0)
const [num,setNum] = useState(1)
const onClick =async ()=>{
setCount(count+1)
await new Promise((resolve,reject)=>{
resolve()
})
setNum(num+1)
}
return (
<div onClick={onClick}>
{count}
{num}
</div>
);
}
看一下setState的源码
mountState,主要是创建了更新队列。dispatchSetState也就是setState,这里的bind,是为了传入currentFiber和queue,那么setState的时候就能拿到是当前的哪一个fiber节点了(因为react的每次更新都是从root开始的,那么是不可能全部fiber节点都跟新一次,只需要跟新对应触发setState的节点就可以了)

disatchState(setState)
isRenderPhaseUpdate判断当前的fiber节点是否在渲染阶段,是的话直接挂载更新队列即可(这里的更新队列有什么用呢,因为每次的render其实都是从根节点开始的,更新队列里其实就标注了那个fiber需要跟新,那么更新时只需跟新对应的fiber即可)
lastRenderedReducer:就是一个处理state的函数,currentState就是useState上存储的state的值(可以看到每次state都会去做更新),里面判断了如果action是一个函数的话就将currentState传入(这也是为什么在setState的回调函数中,可以拿到最新的state的值)并执行,如果action只是一个值的就直接返回。
然后使用Object.is 对比新旧state,相同就返回,否则就调度更新
enqueueConcurrentHookUpdate:将queue加入到全局的更新队列中去,根据当前的节点,向上冒泡寻找根节点(一直调用return属性,直到节点的type为hostFiber)
然后执行scheduleUpdateOnFiber调度更新,这其实也就是之前过挂载流程时的scheduleUpdateOnFiber

scheduleUpdateOnFiber=》ensureRootIsScheduled
这里补充下ensureRootIsScheduled

调度的实现
这里会区分是同步任务还是异步任务,他们的区别在与同步任务会使用微任务调度,异步任务会使用宏任务调度(scheduleMicrotask可以理解为Promise.then(),宏任务并没有用定时器去做的实现,而是使用的window.postMessage(),然后监听message)
这里只看下同步任务的处理,将render加到到任务队列中去
scheduleSyncCallback(performSyncWorkOnRoot.bind(null, root));

flushSyncCallbacks();
其实也就是取任务队列中的callback回调执行,这里的回调执行时异步的,使用的Promise.then(flushSyncCallbacks)

但是多次的setState,会产生多个任务,isFlushingSyncQueue = true;只是为了保证flushSyncCallbacks只会执行一次,所以performSyncWorkOnRoot还是会执行多次的
在performSyncWorkOnRoot中每次执行,都会判断下,所以只有第一次会完全执行,后续state调度在这里都直接返回了,并不会再去render(这里lanes是在commit阶段处理的在layout阶段后处理的)(这也是为什么多次setState只会render一次的原因了)

非特殊说明,本博所有文章均为博主原创。
如若转载,请注明出处:http://www.cx330.cloud/index.php/2025/04/24/react%e4%b8%adsetstate%e5%8e%9f%e7%90%86/
共有 0 条评论