ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

setState&forceUpdate,个人记录

2021-12-03 09:03:24  阅读:181  来源: 互联网

标签:fiber 记录 update next callback updateQueue null forceUpdate setState


setState&forceUpdate

在react中触发状态更新的几种方式:

ReactDOM.render

this.setState

this.forceUpdate

useState

useReducer

我们重点看下重点看下this.setState和this.forceUpdate,hook在第13章讲

this.setState内调用this.updater.enqueueSetState,主要是将update加入updateQueue中

//ReactBaseClasses.jsComponent.prototype.setState=function(partialState, callback) {if(!(typeofpartialState ==='object'||typeofpartialState ==='function'|| partialState ==null)) {    {throwError("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");    }  }this.updater.enqueueSetState(this, partialState, callback,'setState');};

//ReactFiberClassComponent.old.jsenqueueSetState(inst, payload, callback) {constfiber =getInstance(inst);//fiber实例consteventTime =requestEventTime();constsuspenseConfig =requestCurrentSuspenseConfig();constlane =requestUpdateLane(fiber, suspenseConfig);//优先级constupdate =createUpdate(eventTime, lane, suspenseConfig);//创建updateupdate.payload= payload;if(callback !==undefined&& callback !==null) {//赋值回调update.callback= callback;  }enqueueUpdate(fiber, update);//update加入updateQueuescheduleUpdateOnFiber(fiber, lane, eventTime);//调度update}

enqueueUpdate用来将update加入updateQueue队列

//ReactUpdateQueue.old.jsexportfunctionenqueueUpdate(fiber:Fiber,update:Update) {constupdateQueue = fiber.updateQueue;if(updateQueue ===null) {return;  }constsharedQueue:SharedQueue = (updateQueue: any).shared;constpending = sharedQueue.pending;if(pending ===null) {    update.next= update;//与自己形成环状链表}else{    update.next= pending.next;//加入链表的结尾pending.next= update;  }  sharedQueue.pending= update;}

this.forceUpdate和this.setState一样,只是会让tag赋值ForceUpdate

//ReactBaseClasses.jsComponent.prototype.forceUpdate=function(callback) {this.updater.enqueueForceUpdate(this, callback,'forceUpdate');};

//ReactFiberClassComponent.old.jsenqueueForceUpdate(inst, callback) {constfiber =getInstance(inst);consteventTime =requestEventTime();constsuspenseConfig =requestCurrentSuspenseConfig();constlane =requestUpdateLane(fiber, suspenseConfig);constupdate =createUpdate(eventTime, lane, suspenseConfig);//tag赋值ForceUpdateupdate.tag=ForceUpdate;if(callback !==undefined&& callback !==null) {      update.callback= callback;    }enqueueUpdate(fiber, update);scheduleUpdateOnFiber(fiber, lane, eventTime);  },};

如果标记ForceUpdate,render阶段组件更新会根据checkHasForceUpdateAfterProcessing,和checkShouldComponentUpdate来判断,如果Update的tag是ForceUpdate,则checkHasForceUpdateAfterProcessing为true,当组件是PureComponent时,checkShouldComponentUpdate会浅比较state和props,所以当使用this.forceUpdate一定会更新

//ReactFiberClassComponent.old.jsconstshouldUpdate =checkHasForceUpdateAfterProcessing() ||checkShouldComponentUpdate(    workInProgress,    ctor,    oldProps,    newProps,    oldState,    newState,    nextContext,  );

状态更新整体流程

Update&updateQueue

HostRoot或者ClassComponent触发更新后,会在函数createUpdate中创建update,并在后面的render阶段的beginWork中计算Update。FunctionComponent对应的Update在第11章讲,它和HostRoot或者ClassComponent的Update结构有些不一样

//ReactUpdateQueue.old.jsexportfunctioncreateUpdate(eventTime: number, lane: Lane):Update<*> {//创建updateconstupdate:Update<*> = {    eventTime,    lane,tag:UpdateState,payload:null,callback:null,next:null,  };returnupdate;}

我们主要关注这些参数:

lane:优先级(第12章讲)

tag:更新的类型,例如UpdateState、ReplaceState

payload:ClassComponent的payload是setState第一个参数,HostRoot的payload是ReactDOM.render的第一个参数

callback:setState的第二个参数

next:连接下一个Update形成一个链表,例如同时触发多个setState时会形成多个Update,然后用next 连接

对于HostRoot或者ClassComponent会在mount的时候使用initializeUpdateQueue创建updateQueue,然后将updateQueue挂载到fiber节点上

//ReactUpdateQueue.old.jsexportfunctioninitializeUpdateQueue(fiber:Fiber):void{constqueue:UpdateQueue = {baseState: fiber.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared: {pending:null,    },effects:null,  };fiber.updateQueue= queue;}

baseState:初始state,后面会基于这个state,根据Update计算新的state

firstBaseUpdate、lastBaseUpdate:Update形成的链表的头和尾

shared.pending:新产生的update会以单向环状链表保存在shared.pending上,计算state的时候会剪开这个环状链表,并且链接在lastBaseUpdate后

effects:calback不为null的update

从触发更新的fiber节点向上遍历到rootFiber

在markUpdateLaneFromFiberToRoot函数中会从触发更新的节点开始向上遍历到rootFiber,遍历的过程会处理节点的优先级(第15章讲)

//ReactFiberWorkLoop.old.jsfunctionmarkUpdateLaneFromFiberToRoot(    sourceFiber: Fiber,

    lane: Lane,

  ):FiberRoot|null{    sourceFiber.lanes=mergeLanes(sourceFiber.lanes, lane);letalternate = sourceFiber.alternate;if(alternate !==null) {      alternate.lanes=mergeLanes(alternate.lanes, lane);    }letnode = sourceFiber;letparent = sourceFiber.return;while(parent !==null) {//从触发更新的节点开始向上遍历到rootFiberparent.childLanes=mergeLanes(parent.childLanes, lane);//合并childLanes优先级alternate = parent.alternate;if(alternate !==null) {        alternate.childLanes=mergeLanes(alternate.childLanes, lane);      }else{      }      node = parent;      parent = parent.return;    }if(node.tag===HostRoot) {constroot:FiberRoot= node.stateNode;returnroot;    }else{returnnull;    }  }

例如B节点触发更新,B节点被被标记为normal的update,也就是图中的u1,然后向上遍历到根节点,在根节点上打上一个normal的update,如果此时B节点又触发了一个userBlocking的Update,同样会向上遍历到根节点,在根节点上打上一个userBlocking的update。

如果当前根节点更新的优先级是normal,u1、u2都参与状态的计算,如果当前根节点更新的优先级是userBlocking,则只有u2参与计算

调度

在ensureRootIsScheduled中,scheduleCallback会以一个优先级调度render阶段的开始函数performSyncWorkOnRoot或者performConcurrentWorkOnRoot

//ReactFiberWorkLoop.old.jsif(newCallbackPriority ===SyncLanePriority) {// 任务已经过期,需要同步执行render阶段newCallbackNode =scheduleSyncCallback(    performSyncWorkOnRoot.bind(null, root)  );}else{// 根据任务优先级异步执行render阶段varschedulerPriorityLevel =lanePriorityToSchedulerPriority(    newCallbackPriority  );  newCallbackNode =scheduleCallback(    schedulerPriorityLevel,    performConcurrentWorkOnRoot.bind(null, root)  );}

状态更新

classComponent状态计算发生在processUpdateQueue函数中,涉及很多链表操作,看图更加直白

初始时fiber.updateQueue单链表上有firstBaseUpdate(update1)和lastBaseUpdate(update2),以next连接

fiber.updateQueue.shared环状链表上有update3和update4,以next连接互相连接

计算state时,先将fiber.updateQueue.shared环状链表‘剪开’,形成单链表,连接在fiber.updateQueue后面形成baseUpdate

然后遍历按这条链表,根据baseState计算出memoizedState

带优先级的状态更新https://zhuanlan.zhihu.com/p/433044320

https://zhuanlan.zhihu.com/p/433043664

https://zhuanlan.zhihu.com/p/433043381

https://zhuanlan.zhihu.com/p/433043007

https://zhuanlan.zhihu.com/p/433041463

https://zhuanlan.zhihu.com/p/433146161

https://zhuanlan.zhihu.com/p/433150652

https://zhuanlan.zhihu.com/p/433149942

https://zhuanlan.zhihu.com/p/433151111

https://zhuanlan.zhihu.com/p/433152925

类似git提交,这里的c3意味着高优先级的任务,比如用户出发的事件,数据请求,同步执行的代码等。

通过ReactDOM.render创建的应用没有优先级的概念,类比git提交,相当于先commit,然后提交c3

在concurrent模式下,类似git rebase,先暂存之前的代码,在master上开发,然后rebase到之前的分支上

优先级是由Scheduler来调度的,这里我们只关心状态计算时的优先级排序,也就是在函数processUpdateQueue中发生的计算,例如初始时有c1-c4四个update,其中c1和c3为高优先级

在第一次render的时候,低优先级的update会跳过,所以只有c1和c3加入状态的计算

在第二次render的时候,会以第一次中跳过的update(c2)之前的update(c1)作为baseState,跳过的update和之后的update(c2,c3,c4)作为baseUpdate重新计算

在在concurrent模式下,componentWillMount可能会执行多次,变现和之前的版本不一致

注意,fiber.updateQueue.shared会同时存在于workInprogress Fiber和current Fiber,目的是为了防止高优先级打断正在进行的计算而导致状态丢失,这段代码也是发生在processUpdateQueue中

看demo_8的优先级

现在来看下计算状态的函数

//ReactUpdateQueue.old.jsexportfunctionprocessUpdateQueue(workInProgress:Fiber,props: any,instance: any,renderLanes:Lanes,):void{constqueue:UpdateQueue = (workInProgress.updateQueue: any);  hasForceUpdate =false;letfirstBaseUpdate = queue.firstBaseUpdate;//updateQueue的第一个UpdateletlastBaseUpdate = queue.lastBaseUpdate;//updateQueue的最后一个UpdateletpendingQueue = queue.shared.pending;//未计算的pendingQueueif(pendingQueue !==null) {    queue.shared.pending=null;constlastPendingUpdate = pendingQueue;//未计算的ppendingQueue的最后一个updateconstfirstPendingUpdate = lastPendingUpdate.next;//未计算的pendingQueue的第一个updatelastPendingUpdate.next=null;//剪开环状链表if(lastBaseUpdate ===null) {//将pendingQueue加入到updateQueuefirstBaseUpdate = firstPendingUpdate;    }else{      lastBaseUpdate.next= firstPendingUpdate;    }    lastBaseUpdate = lastPendingUpdate;constcurrent = workInProgress.alternate;//current上做同样的操作if(current !==null) {constcurrentQueue:UpdateQueue = (current.updateQueue: any);constcurrentLastBaseUpdate = currentQueue.lastBaseUpdate;if(currentLastBaseUpdate !== lastBaseUpdate) {if(currentLastBaseUpdate ===null) {          currentQueue.firstBaseUpdate= firstPendingUpdate;        }else{          currentLastBaseUpdate.next= firstPendingUpdate;        }        currentQueue.lastBaseUpdate= lastPendingUpdate;      }    }  }if(firstBaseUpdate !==null) {letnewState = queue.baseState;letnewLanes =NoLanes;letnewBaseState =null;letnewFirstBaseUpdate =null;letnewLastBaseUpdate =null;letupdate = firstBaseUpdate;do{constupdateLane = update.lane;constupdateEventTime = update.eventTime;if(!isSubsetOfLanes(renderLanes, updateLane)) {//判断优先级是够足够constclone:Update = {//优先级不够 跳过当前updateeventTime: updateEventTime,lane: updateLane,tag: update.tag,payload: update.payload,callback: update.callback,next:null,        };if(newLastBaseUpdate ===null) {//保存跳过的updatenewFirstBaseUpdate = newLastBaseUpdate = clone;          newBaseState = newState;        }else{          newLastBaseUpdate = newLastBaseUpdate.next= clone;        }        newLanes =mergeLanes(newLanes, updateLane);      }else{//直到newLastBaseUpdate为null才不会计算,防止updateQueue没计算完if(newLastBaseUpdate !==null) {constclone:Update = {eventTime: updateEventTime,lane:NoLane,tag: update.tag,payload: update.payload,callback: update.callback,next:null,          };          newLastBaseUpdate = newLastBaseUpdate.next= clone;        }        newState =getStateFromUpdate(//根据updateQueue计算stateworkInProgress,          queue,          update,          newState,          props,          instance,        );constcallback = update.callback;if(callback !==null) {          workInProgress.flags|=Callback;//Callback flagconsteffects = queue.effects;if(effects ===null) {            queue.effects= [update];          }else{            effects.push(update);          }        }      }      update = update.next;//下一个updateif(update ===null) {//重置updateQueuependingQueue = queue.shared.pending;if(pendingQueue ===null) {break;        }else{constlastPendingUpdate = pendingQueue;constfirstPendingUpdate = ((lastPendingUpdate.next: any):Update);          lastPendingUpdate.next=null;          update = firstPendingUpdate;          queue.lastBaseUpdate= lastPendingUpdate;          queue.shared.pending=null;        }      }    }while(true);if(newLastBaseUpdate ===null) {      newBaseState = newState;    }    queue.baseState= ((newBaseState: any):State);//新的statequeue.firstBaseUpdate= newFirstBaseUpdate;//新的第一个updatequeue.lastBaseUpdate= newLastBaseUpdate;//新的最后一个updatemarkSkippedUpdateLanes(newLanes);    workInProgress.lanes= newLanes;    workInProgress.memoizedState= newState;  }//...}

标签:fiber,记录,update,next,callback,updateQueue,null,forceUpdate,setState
来源: https://blog.csdn.net/chunzhenwang666/article/details/121690932

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有