You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
functionmountWorkInProgressHook(): Hook{consthook: Hook={memoizedState: null,baseState: null,baseQueue: null,queue: null,next: null,};// hook对象的结构 是构成 hooks链表的最小单元if(workInProgressHook===null){// This is the first hook in the list/** * 这里可以看到,fiber 中的 memoizedState 是用来存储 hooks 链表的 */currentlyRenderingFiber.memoizedState=workInProgressHook=hook;}else{// Append to the end of the list/** * 后续在调用其他 hook 时,会直接接入到上一个 hook 对象的后面 */workInProgressHook=workInProgressHook.next=hook;}returnworkInProgressHook;}
functionupdateWorkInProgressHook(): Hook{
let nextCurrentHook: null|Hook;// 根据注释 current hook list is the list that belongs to the current fiber 可知,current hook 指向了 current fiber 树上的 hooks 链表// 毕竟更新操作时,必然已经存在了 current 树// currentHook在函数组件调用完成时会被设置为null因此可以用来确认是否是刚开始重新渲染if(currentHook===null){/** * 根据注释 The work-in-progress fiber. I've named it differently to distinguish it from the work-in-progress hook. * currentlyRenderingFiber是一个 WIP 的 fiber * 因此它的 alternate 是指向其对应的 current fiber 节点 */constcurrent=currentlyRenderingFiber.alternate;if(current!==null){// current节点存在,将nextCurrentHook指向current.memoizedState(hooks 链表)nextCurrentHook=current.memoizedState;}else{nextCurrentHook=null;}}else{// 指针后移nextCurrentHook=currentHook.next;}
let nextWorkInProgressHook: null|Hook;if(workInProgressHook===null){nextWorkInProgressHook=currentlyRenderingFiber.memoizedState;}else{nextWorkInProgressHook=workInProgressHook.next;}if(nextWorkInProgressHook!==null){// There's already a work-in-progress. Reuse it.workInProgressHook=nextWorkInProgressHook;nextWorkInProgressHook=workInProgressHook.next;currentHook=nextCurrentHook;}else{// Clone from the current hook.invariant(nextCurrentHook!==null,'Rendered more hooks than during the previous render.',);currentHook=nextCurrentHook;constnewHook: Hook={memoizedState: currentHook.memoizedState,baseState: currentHook.baseState,baseQueue: currentHook.baseQueue,queue: currentHook.queue,next: null,};if(workInProgressHook===null){// This is the first hook in the list.currentlyRenderingFiber.memoizedState=workInProgressHook=newHook;}else{// Append to the end of the list.workInProgressHook=workInProgressHook.next=newHook;}}returnworkInProgressHook;}
代码冗长,指针又跳来跳去,看懂的最好办法还是自己调试一下。这里最重要的信息其实就是,在更新时会借 current 节点上的 hooks 链表去生成新的 hooks 链表。至于如何更新状态不在本文内介绍。
React Hooks: hooks 链表
文章的React版本基于v17.02的concurrent模式
hooks链表是在讲解所有hook前绕不开的话题,此文将以调试的角度出发分析hooks链表的结构与作用。
组件挂载时的链表逻辑
首先我们试着对这段代码进行录制。
从结果中我们不难注意到renderWithHooks这一函数,该函数内执行了UseEffectAnduseLayoutEffect,函数式组件作为函数被调用本身就是渲染的一环,除此之外在这个函数中也调用到了代码中的hook。
在 React 内根据是否存在 current 来判断是挂载还是更新是一个比较常见的操作。由于我们目前记录的是初次挂载时的操作,因此此时 ReactCurrentDispatcher.current 为 HooksDispatcherOnMount 。至于为什么需要区分挂载/更新,后续会介绍。
接下来我们关注在UseEffectAnduseLayoutEffect内调用的 useState 与 useEffect 。我们将进入构筑 hooks 链表的相关逻辑。
在 UseEffectAnduseLayoutEffect 这一例子中最先被调用的是 useState ,而对其进行断点调试会进入 mountState 。
但是在其代码中我们目前只需要关注 mountWorkInProgressHook ,这是用来构建 hook 对象的
每调用一次 hooks 函数都会创建一个 hook 对象,其结构如下:
上述代码中请注意 workInProgressHook 这一个指针,它反映了当前组件中调用到了哪一个 hook 函数。
当代码中的 useState、useEffect、useLayout 全部执行完之后我们会获取到一条完整的 hooks 链表。
此时也可以看到 workInProgressHook 确实指向了最后一个 hook 对象
组件更新时的链表逻辑
录制一下触发点击事件的记录,得到以下的结果。
此时 ReactCurrentDispatcher.current 为 HooksDispatcherOnUpdate。因此再次进入 UseEffectAnduseLayoutEffect 时调用的 hooks 将与挂载时不同。此时再次对 useState 打断点将进入 updateState 内的 updateWorkInProgressHook
代码冗长,指针又跳来跳去,看懂的最好办法还是自己调试一下。这里最重要的信息其实就是,在更新时会借 current 节点上的 hooks 链表去生成新的 hooks 链表。至于如何更新状态不在本文内介绍。
总结
The text was updated successfully, but these errors were encountered: