跳到主要内容

认识

2023年06月10日
柏拉文
越努力,越幸运

一、认识


二、数据结构


2.1 hook

const hook = {
next: null,
baseQueue: null,
baseState: null,
updateQueue: null,
memoizedState: null
};

2.2 hook.memoizedState

hook.memoizedState 用于存储 { current: xxx} 对象

2.3 fiber.memoizedState

fiber.memoizedState 用于存储 hooks 链表

三、执行函数组件


React.jsbeginWork 阶段, 遇到 FunctionComponent 类型的 Fiber, 会执行 updateFunctionComponentupdateFunctionComponent 内部调用 renderWithHooks。主要工作如下:

  1. 重置状态: 函数组件执行前, 重置当前正在处理的 hook、当前正在使用的 hook

  2. 记录当前组件 fiber: 通过 currentlyRenderingFiber 变量记录当前组件 fiber

  3. 选择对应阶段的 Hook: mount 阶段选择 HooksDispatcherOnMount 作为 Hooks Map, update 阶段选择 HooksDispatcherOnUpdate 作为 Hooks Map函数组件mount 阶段 创建 hook 数据结构, 存储到 fiber.memoizedState 或者 hook.next, 初次建立其 hooksfiber 之间的关系。函数组件 update 阶段 找到当前函数组件 fiber, 找到当前 hook, 更新 hook 状态。

  4. 执行函数组件: 执行我们真正函数组件,所有的 hooks 将依次执行, 每个 hook 内部要根据 currentlyRenderingFiber 读取对应的内容,

  5. 重置状态: 执行完 Component, 立马重置 currentlyRenderingFiber, 防止函数组件外部调用。置当前正在处理的 hook、当前正在使用的 hook

3.1 /packages/react-reconciler/fiberHooks.js

export function renderWithHooks(workInProgress, Component, lane) {
renderLane = lane;
currentlyRenderingFiber = workInProgress;
workInProgress.memoizedState = null;
workInProgress.updateQueue = null;

const current = workInProgress.alternate;

if (current !== null) {
// update
currentDispatcher.current = HooksDispatcherOnUpdate;
} else {
// mount
currentDispatcher.current = HooksDispatcherOnMount;
}

const props = workInProgress.pendingProps;
const children = Component(props);

currentHook = null;
renderLane = NoLane;
workInProgressHook = null;
currentlyRenderingFiber = null;

return children;
}

3.2 /packages/react-reconciler/fiberHooks.js

/**
* @description: Mount 阶段 Hooks 实现
*/
const HooksDispatcherOnMount = {
use,
useRef: mountRef,
useMemo: mountMemo,
useState: mountState,
useEffect: mountEffect,
useContext: readContext,
useCallback: mountCallback,
useTransition: mountTransition
};

/**
* @description: Update 阶段 Hooks 实现
*/
const HooksDispatcherOnUpdate = {
use,
useRef: updateRef,
useMemo: updateMemo,
useState: updateState,
useEffect: updateEffect,
useContext: readContext,
useCallback: updateCallback,
useTransition: updateTransition
};

四、函数组件 mount 阶段 Hook


useStatemount 阶段创建当前 hook, 处理初始值, 创建 updateQueue 数据结构, hook.updateQueue 保存已创建好的 updateQueue, hook.memoizedState 存储初始值。通过 dispatchSetState 创建 dispatch 方法, 同 初始值 一同以数组的形式返回。

返回的形式是数组, 而不是对象: 因为数组解构时可以任意命名, 而对象解构需要属性名一致, 不方便。

创建 dispatchSetState 的主要逻辑为: dispatchSetState 通过 .bind 方法, 预先传入 fiberupdateQueue, 当用户调用 dispatch 时, 传入一个值, 这个作为 action 参数传入。在 dispatchSetState 中通过 action 和当前优先级创建 update 数据结构, 随后进行 eagerState 优化策略, 如果前后数据无变化, 只需将 update 加入 updateQueue.shared.pending 中皆可, 并且更新优先级为 NoLane。如果前后数据有变化, 在update 加入 updateQueue.shared.pending 的同时, 以传入的 Lane 优先级, 调用 scheduleUpdateOnFiber 开启调度更新流程。 其中 update 加入 updateQueue.shared.pending 中形成环状链表

五、函数组件 update 阶段 Hook


三、工作流程


3.1 标记 Ref

标记 Ref 需要满足:

  • mount: 存在 Ref

  • update: Ref 引用变化

标记的时机包括:

  • beginWork

  • completeWork

3.2 执行 Ref

对于正常的绑定操作:

  • 解绑之前的 Ref

  • 绑定新的 Ref

对于组件卸载:

  • 解绑之前的 Ref

四、思考与沉淀