react hooks的更新核心,其实是一个链表的钩子。
目前可以确定,useRef和useState类似,每一个useState/useRef都可以理解为一个钩子,钩子(指针)存储在workInProgressHook中
可以回顾一下useRef的源码:react hooks本质探索 - useRef源码详解
初始化workInProgressHook代码如下(中文注释为笔者添加的注释,英文注释为源代码里的注释)
function mountWorkInProgressHook() {
// 新建链表结点
var hook = {
memoizedState: null,
baseState: null,
baseQueue: null,
queue: null,
next: null
};
// workInProgressHook可以理解为链表的指针
// 一般情况:指针指的不是链表的开头
if (workInProgressHook !== null) {
// 把新的hook赋值给当前workInProgressHook的next
workInProgressHook.next = hook;
// 让当前的指针指向下一个钩子
workInProgressHook = workInProgressHook.next
}
// 特殊情况:指针指的是null,说明链表未建立。
if (workInProgressHook === null) {
// 让指针指向hook
workInProgressHook = hook;
// 这里先不去管他,currentlyRenderingFiber$1应该是优化渲染部分的
currentlyRenderingFiber$1.memoizedState = workInProgressHook;
}
// 返回指针
return workInProgressHook;
}
总结来说,等于是把链表的next指向一个新的hook,同时返回了这个链表的表尾。
以上就是初始化useRef/useState干的事情。
接下来看更新时,useRef/useState干了什么:
因为代码比较长,可以先看一个步骤图了解一下步骤拆解
下面看源码(中文注释为笔者添加的注释,英文注释为源代码里的注释):
function updateWorkInProgressHook() {
// This function is used both for updates and for re-renders triggered by a
// render phase update. It assumes there is either a current hook we can
// clone, or a work-in-progress hook from a previous render pass that we can
// use as a base. When we reach the end of the base list, we must switch to
// the dispatcher used for mounts.
var nextCurrentHook;
// 这里在workInProgressHook之外,引入了一个新的概念:currentHook
// 但是这个概念可以不用管,因为这是fiber的概念。我们本次不需要知道fiber的概念
if (currentHook === null) {
var current = currentlyRenderingFiber$1.alternate;
if (current !== null) {
nextCurrentHook = current.memoizedState;
} else {
nextCurrentHook = null;
}
} else {
nextCurrentHook = currentHook.next;
}
// 来到我们熟悉的workInProgressHook
var nextWorkInProgressHook;
// 以下这块代码,是为了初始化nextWorkInProgressHook
// 特殊情况:链表开头。如果当前的指针指向为空,说明为开头。注意这里判断的是workInProgressHook
if (workInProgressHook === null) {
// 为什么这么赋值,也跟fiber有关。可以先不管
nextWorkInProgressHook = currentlyRenderingFiber$1.memoizedState;
} else {
// 如果不是链表开头,那么会让nextWorkInProgressHook等于当前链表指针的next,很好理解
nextWorkInProgressHook = workInProgressHook.next;
}
// 这个模块是为了让workInProgressHook正常地指向下一个链表节点
// 正常情况:初始化nextWorkInProgressHook后,nextWorkInProgressHook不为空
if (nextWorkInProgressHook !== null) {
// 这部分逻辑就是正常将workInProgressHook指针指向下一个元素
// There's already a work-in-progress. Reuse it.
workInProgressHook = nextWorkInProgressHook;
// nextWorkInProgressHook在这个方法里不再用到,但是一个全局变量,其他地方可能用到。
nextWorkInProgressHook = workInProgressHook.next;
currentHook = nextCurrentHook;
} else {
// 特殊情况:初始化nextWorkInProgressHook后,nextWorkInProgressHook仍然为空
// 可能有两种情况:
// 1.workInProgressHook为空(代表链表为空),currentlyRenderingFiber$1.memoizedState为空
// 2.workInProgressHook不为空,当前指针指向了链表的尾部,所以workInProgressHook.next为空,导致nextWorkInProgressHook为空。
// 不管链表为空,还是指向了结尾,抽象意义上都是指向了链表结尾。
// 这两种情况,最后都执行了workInProgressHook = newHook;
// 这一块都是处理fiberNode,可以不管
// Clone from the current hook.
if (!(nextCurrentHook !== null)) {
{
throw Error( "Rendered more hooks than during the previous render." );
}
}
currentHook = nextCurrentHook;
var newHook = {
memoizedState: currentHook.memoizedState,
baseState: currentHook.baseState,
baseQueue: currentHook.baseQueue,
queue: currentHook.queue,
next: null
};
// 处理链表为空的情况
// 这里要注意,如果nextWorkInProgressHook不为空,那么workInProgressHook也一定不为空。所以这里隐含了一个条件:
// workInProgressHook和nextWorkInProgressHook都为空。
if (workInProgressHook === null) {
// 这里稍稍改写下,把连续赋值分成了两部分
// 链表为空,就需要把第一个元素赋值给指针,让链表指针正常
// This is the first hook in the list.
workInProgressHook = newHook;
// fiber相关
currentlyRenderingFiber$1.memoizedState = workInProgressHook;
} else {
// 当前指针指向了链表的尾部的情况
// 这里做了简单的改写
// Append to the end of the list.
workInProgressHook.next = newHook;
// 可以看到,workInProgressHook = newHook是必定执行的。也要注意,这里是在两个else里。
workInProgressHook = newHook;
}
}
return workInProgressHook;
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。