setState被调用后最终会走到scheduleUpdateOnFiber函数中

这段代码的输出结果,第一个console.log会输出data,而第二个console.log会输出setTimeout。也就是第一次setState的时候,是异步更新的,而第二次setState的时候,它又变成了同步更新,是不是有点晕呢?我们去源码里看一下setState更新调度的时候到底做了些什么。

探针

setState被调用后最终会走到scheduleUpdateOnFiber函数中,那我们看一下这个函数做了些什么呢?

typescript

if(executionContext ===NoContext) {// Flush the synchronous work now, unless we're already working or inside// a batch. This is intentionally inside scheduleUpdateOnFiber instead of// scheduleCallbackForFiber to preserve the ability to schedule a callback// without immediately flushing it. We only do this for user-initiated// updates, to preserve historical behavior of legacy mode.flushSyncCallbackQueue();}

executionContext代表了React目前所处的阶段,而NoContext你可以理解为是React没活干的状态,而flushSyncCallbackQueue里面就会去同步调用我们的this.setState,也就是说同步更新我们的state。所以,我们已经知道了,当executionContext为NoContext的时候,我们的setState就是同步的。那什么地方会改变executionContext的值呢?

我们随便找几个地方看看:

typescript

functionbatchedEventUpdates$1(fn, a) {varprevExecutionContext = executionContext;executionContext |=EventContext;// ...省略}functionbatchedUpdates$1(fn, a) {varprevExecutionContext = executionContext;executionContext |=BatchedContext;// ...省略}

当React进入它自己的调度步骤时,会给executionContext赋予不同的枚举,表示不同的操作和目前React所处的调度状态,而executionContext的初始值就是NoContext,所以只要你不进入React的调度流程,这个值就是NoContext,那你的setState就是同步的。

那在useState呢?自从React出了hooks之后,函数组件也能拥有自己的状态,那么如果我们调用它的第二个参数去setState更改状态,和类组件的this.setState是一样的效果吗?

没错,因为useState的set函数最终也会走到scheduleUpdateOnFiber,所以在这一点上和this.setState是没有区别的,相当于使用了一个通用函数。

但是值得注意的是,当我们调用this.setState的时候,React会自动帮我们做一个state的合并,而hook则不会,所以我们在使用的时候更着重注意这一点。

举个例子:

patek-hzs.jshdwatch.com

patek-bjs.xajshd.com

patek-sz.kmjshd.com

patek-hzs.hebjshd.com

patek-bjs.watchrft.cn

patekw.jsfltime.com

patekw.watchec.cn

patekw.nnjshd.com

patekw.richardweixiu.com

patekw.ytjshd.com

patekw.watchjwj.cn

patekw.swatchkb.top

patekw.ywbzn.com

patek-cds.hidcwatch.com

patek-njs.ywbzn.com

patek-beijing.watch4s.com

patek-beijing.wbiaohome.com

patek-beijing.wbiao120.com

constantin-shs.fdcpx.net

constantin-bjs.fdcpx.net

constantin-shenzhen.biaoshouhou.cn

constantin-gzs.biaoshouhou.cn

constantin-shs.audemarsweixiu.com

constantin-bjs.audemarsweixiu.com

constantin-shenzhen.hidcwatch.com

constantin-gzs.hidcwatch.com

constantin-shs.fjfsx.com

constantin-bjs.fjfsx.com

constantin-shenzhen.hntwx.cn

constantin-gzs.hntwx.cn

constantin-shs.hx626.com

constantin-bjs.hx626.com

constantin-shenzhen.watchjwf.cn

constantin-gzs.watchjwf.cn

constantin-shs.shjshdzb.com

constantin-bjs.shjshdzb.com

constantin-shenzhen.shmwatch.cn

constantin-gzs.shmwatch.cn

constantin-shs.gyjshd.com

constantin-bjs.gyjshd.com

constantin-shenzhen.zhcxb.cn

constantin-gzs.zhcxb.cn

constantin-shenzhen.jshdvip.com

constantin-gzs.jshdvip.com

constantin-shs.gyjshdzb.com

constantin-bjs.gyjshdzb.com

constantin-sys.jhpwd.cn

constantin-zzs.jhpwd.cn

constantin-shenzhen.wzjshd.com

constantin-gzs.wzjshd.com

typescript

// 类组件中state = {data:"data",data1:"data1",};this.setState({data:"new data"});console.log(state);// { data: 'new data',data1: 'data1' }// 函数组件中const[state, setState] =useState({data:"data",data1:"data1"});setState({data:"new data"});console.log(state);// { data: 'new data' }

但是如果你自己去尝试在函数组件中的setTimeout中去调用setState之后,打印state,你会发现并没有改变,这时你就会很疑惑,为什么呢?这不是同步执行的么?这其实是一个闭包问题,实际上拿到的还是上一个state,那打印出来的值自然也还是上一次的,此时真正的state已经改变了。

相信看到这里对于标题你已经有了答案了吧?只要你进入了React的调度流程,那就是异步的。只要你没有进入React的调度流程(executionContext === NoContext),那就是同步的。什么情况不会进入React的调度流程?setTimeout、setInterval,直接在DOM上绑定原生事件等。这些都不会走React的调度流程,你在这种情况下调用setState,那这次setState就是同步的。否则就是异步的。而setState同步执行的情况下,DOM也会被同步执行更新,也就意味着如果多次setState会导致多次更新,这也是毫无意义且浪费性能的。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容