Hook的一些基本知识
首先在遍历函数时会将hook相关的变量赋值,赋值给ReactCurrentDispatcher.current对象,然后调用函数组建,这样函数内调用Hook的就会使用对应的方法,在函数组建执行之后,就将之前的Hook对象重新赋值给ReactCurrentDispatcher.current,这样函数外调用Hook就会报错。初始化的Hook函数就是抛出一些错误,控制台内会打印出异常。
在不同阶段中,会将ReactCurrentDispatcher.current赋值给不同的对象属性,即看起来使用的是同一个方法,但在代码底层中已经变更了核心方法,所以在不同阶段会有不同的效果。
hook的一些常规用法
在绝大多数的hook都会创建hook对象,在mount阶段调用mountWorkInProgressHook方法,
// 创建一个全局hook对象
let workInProgressHook = null
// 以下是 mountWorkInProgressHook 的代码
const hook = {
memoizedState: null,
baseState: null,
baseQueue: null,
queue: null,
next: null,
};
// 是否是当前函数的第一个hook
if(workInProgressHook !== null){
fiber.memoizedState = workInProgressHook = hook
}else{
// 后续创建的hook都放在上一个hook.next对象
workInProgressHook = workInProgressHook.next = hook
}
return workInProgressHook;
// 当执行完这个函数组件之后
workInProgressHook = null;
与此对应的是update阶段创建hook对象,调用updateWorkInProgressHook方法,
创建一个全局属性
let currentHook = null
// 以下是updateWorkInProgressHook
let nextCurrentHook;
if(currentHook === null){
const current = fiber.alternate;
if(current !== null){
// 当前fiber 第一个hook,获取另外一条fiber树上的hook链表
nextCurrentHook = current.memoizedState;
} else {
// 异常状态
nextCurrentHook = null;
}
} else {
// 获取链表中下一个hook对象,第二次及其以后的hook
nextCurrentHook = currentHook.next;
}
currentHook = nextCurrentHook;
// 浅copy hook对象
const newHook = {
memoizedState: currentHook.memoizedState,
baseState: currentHook.baseState,
baseQueue: currentHook.baseQueue,
queue: currentHook.queue,
next: null
};
// 重新更新hook链表对象
if (workInProgressHook === null) {
fiber.memoizedState = workInProgressHook = newHook;
} else {
workInProgressHook = workInProgressHook.next = newHook;
}
return workInProgressHook;
// 当执行完这个函数组件之后
currentHook = null;
updateWorkInProgressHook 是源码简化之后的,有一些逻辑处理不会在常规下使用到的兼容,所以就直接省略。
上面说绝大多数的hook都会创建hook对象,为什么说绝大多数,比如useContext就会创建hook对象,所以上面的代码就不适用。
hook的检查逻辑
每个hook调用都会有hook检查机制,为了避免出现hook在if或者循环语句中。
// 全局变量中
let currentHookNameInDev = null;
let hookTypesDev = null;
let hookTypesUpdateIndexDev = -1;
// mount阶段调用 mountHookTypesDev 方法
const hookName = currentHookNameInDev;
if (hookTypesDev === null) {
hookTypesDev = [hookName];
} else {
hookTypesDev.push(hookName);
}
// update阶段调用updateHookTypesDev方法检查
const hookName = currentHookNameInDev;
if (hookTypesDev !== null) {
hookTypesUpdateIndexDev++;
if (hookTypesDev[hookTypesUpdateIndexDev] !== hookName) {
// console.error 方法报错
warnOnHookMismatchInDev(hookName);
}
}
hook的检查机制就是一个hook数组,对比前后两次hook数组的hookName是否相同。
currentHookNameInDev是useXxx的name。
// todo pushEffect 和生命周期相关的创建