当前位置: 首页 > Web前端 > JavaScript

Reacthooks源码核心:workInProgressHook函数的更新核心

时间:2023-03-27 15:37:55 JavaScript

reacthooks其实是一个链表hook。目前可以确定useRef类似于useState。每个useState/useRef都可以理解为一个hook。钩子(指针)存储在workInProgressHook中。可以查看useRef的源码:Reacthooks本质探索-useRef源码详细初始化workInProgressHook代码如下(中文注释为作者添加的注释,英文注释为源码中的注释)functionmountWorkInProgressHook(){//创建一个新的链表节点varhook={memoizedState:null,baseState:null,baseQueue:null,queue:null,next:null};//workInProgressHook可以理解为链表的指针//一般情况:指针不指向链表的开头if(workInProgressHook!==null){//将新的钩子赋值给下一个当前workInProgressHookworkInProgressHook.next=hook;//让当前指针指向下一个钩子workInProgressHook=workInProgressHook.next}//特殊情况:指针指向null,说明链表还没有建立。if(workInProgressHook===null){//让指针指向钩子workInProgressHook=hook;//不用管它,currentlyRenderingFiber$1应该是currentlyRenderingFiber$1.memoizedState=workInProgressHook;}//返回指针returnworkInProgressHook;}综上所述,相当于将链表的next指向一个新的钩子,同时返回链表的尾部。以上就是初始化useRef/useState所做的事情。接下来看看useRef/useState在update的时候做了什么:因为代码比较长,可以先看一个步骤图了解一下步骤的反汇编再看源码(中文注释是自己加的注释作者,英文注释是源代码中的注释):functionupdateWorkInProgressHook(){//此函数用于更新和由//渲染阶段更新触发的重新渲染。它假设有一个我们可以//克隆的当前挂钩,或者我们可以//用作基础的来自先前渲染通道的正在进行的挂钩。当我们到达基本列表的末尾时,我们必须切换到//用于挂载的调度程序。varnextCurrentHook;//这里在workInProgressHook之外,引入了一个新的概念:currentHook//但是这个概念可以忽略,因为这是fiber的概念。这次我们不需要知道fiber的概念if(currentHook===null){varcurrent=currentlyRenderingFiber$1.alternate;如果(当前!==null){nextCurrentHook=current.memoizedState;}else{nextCurrentHook=null;}}else{nextCurrentHook=currentHook.next;}//来到熟悉的workInProgressHookvarnextWorkInProgressHook;//下面的代码是初始化nextWorkInProgressHook//特例:链表的开头。如果当前指针指向NULL,则表示开始。注意这里的判断是workInProgressHookif(workInProgressHook===null){//为什么这个赋值也和fiber有关。你可以忽略nextWorkInProgressHook=currentlyRenderingFiber$1.memoizedState;}else{//如果不是链表的开头,则让nextWorkInProgressHook等于当前链表指针的下一个,很容易理解nextWorkInProgressHook=workInProgressHook.next;}//该模块是让workInProgressHook正常指向下一个链表节点//正常情况:初始化nextWorkInProgressHook后,nextWorkInProgressHook不为空if(nextWorkInProgressHook!==null){//这部分逻辑是正常指向指向下一个元素的workInProgressHook指针//nextWorkInProgressHook在该方法中不再使用,但它是一个全局变量,可能在其他地方使用。nextWorkInProgressHook=workInProgressHook.next;currentHook=nextCurrentHook;}else{//特殊情况:初始化nextWorkInProgressHook后,nextWorkInProgressHook仍然为空//可能有两种情况://1.workInProgressHook为空(代表链表为空),currentlyRenderingFiber$1.memoizedState为空//2.workInProgressHook不为空,当前指针指向链表尾部,所以workInProgressHook.next为空,导致nextWorkInProgressHook为空。//不管链表为空还是指向末尾,抽象意义上都指向链表的末尾。//在这两种情况下,workInProgressHook=newHook在最后执行;//这部分是处理fiberNode,可以忽略//从当前钩子克隆。if(!(nextCurrentHook!==null)){{throwError("渲染的hooks比上次渲染时多。");}}currentHook=nextCurrentHook;varnewHook={memoizedState:currentHook.memoizedState,baseState:currentHook.baseState,baseQueue:currentHook.baseQueue,queue:currentHook.queue,next:null};//处理链表为空的情况//这里要注意,如果nextWorkInProgressHook不为空,那么workInProgressHook也必须不为空。所以这是一个隐含的条件://workInProgressHook和nextWorkInProgressHook都是空的。if(workInProgressHook===null){//这里稍微改写一下,将连续赋值分成两部分//如果链表为空,需要将第一个元素赋值给指针,做成链表pointernormal//这是列表中的第一个钩子。workInProgressHook=newHook;//Fiber相关currentlyRenderingFiber$1.memoizedState=workInProgressHook;}else{//当前指针指向链表的末尾//这里是一个简单的重写//追加到链表的末尾.workInProgressHook.next=newHook;//正如你所看到的,必须执行workInProgressHook=newHook。另请注意,这是另外两个。workInProgressHook=newHook;}}返回workInProgressHook;}