跳到主要内容

认识

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

一、认识


useMemo 缓存变量数据,当组件重新渲染时不会重新创建变量。(防止父组件重新渲染导致变量数据发生变化)。用法如下所示:

const cachedValue = useMemo(calculateValue, dependencies)

二、细节


2.1 hook

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

2.2 hook.memoizedState

hook.memoizedState 用于存储 [callback(), depA] 数组

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


mount 阶段: 创建 hook 对象, 执行用户传递的 nextCreate 参数, 将 nextCreate 执行结果和 deps 以数组的形式存储至 hook.memoizedState 中。最后返回 nextCreate 执行结果。

function mountMemo(nextCreate, deps) {
const hook = mountWorkInProgressHook();
const nextDeps = deps === undefined ? null : deps;
const nextValue = nextCreate();
hook.memoizedState = [nextValue, nextDeps];
return nextValue;
}

五、函数组件 update 阶段 Hook


update 阶段: 获取当前 hook 对象, 读取当前 hook 对象的 memoizedState 值, 对比存储在 memoizedState 中的 deps 与用户传递的 deps, 如果相等, 直接返回 memoizedState 数组的第一个值。如果不相等, 执行用户传递的 nextCreate 参数, 将 nextCreate 执行结果和 deps 以数组的形式存储至 hook.memoizedState 中。最后返回 nextCreate 执行结果。

function updateMemo(nextCreate, deps) {
const hook = updateWorkInProgressHook();
const nextDeps = deps === undefined ? null : deps;
const prevState = hook.memoizedState;

if (nextDeps !== null) {
const prevDeps = prevState[1];
if (areHookInputsEqual(nextDeps, prevDeps)) {
return prevState[0];
}
}

const nextValue = nextCreate();
hook.memoizedState = [nextValue, nextDeps];
return nextValue;
}