useMemo的使用有两种情况:挂载和更新,因此useMemo有两种实现:mountMemo和updateMemo。
1. mountMemo的源代码:
function mountMemo<T>(
nextCreate: () => T,
deps: Array<mixed> | void | null,
): T {
const hook = mountWorkInProgressHook();
const nextDeps = deps === undefined ? null : deps;
const nextValue = nextCreate();
if (shouldDoubleInvokeUserFnsInHooksDEV) {
setIsStrictModeForDevtools(true);
nextCreate();
setIsStrictModeForDevtools(false);
}
hook.memoizedState = [nextValue, nextDeps];
return nextValue;
}
解释:
在挂载阶段,useMemo函数调用回调函数计算并返回值。将值和依赖项保存到 hook.memoizedState 中。
-
使用
mountWorkInProgressHook创建一个钩子对象(hook object)。 -
将 deps(依赖项数组) 保存在
nextDeps中。 -
调用
nextCreate()获取nextValue(缓存的回调函数的计算值)。如果在开发环境中,调用两次该函数,便于调试。 -
将
nextValue和nextDeps保存到hook.memoizedState(该对象是核心,具有记忆功能,可以缓存之前的值)中并返回nextValue。
2. updateMemo的源代码:
function updateMemo<T>(
nextCreate: () => T,
deps: Array<mixed> | void | null,
): T {
const hook = updateWorkInProgressHook();
const nextDeps = deps === undefined ? null : deps;
const prevState = hook.memoizedState;
// Assume these are defined. If they're not, areHookInputsEqual will warn.
if (nextDeps !== null) {
const prevDeps: Array<mixed> | null = prevState[1];
if (areHookInputsEqual(nextDeps, prevDeps)) {
return prevState[0];
}
}
const nextValue = nextCreate();
if (shouldDoubleInvokeUserFnsInHooksDEV) {
setIsStrictModeForDevtools(true);
nextCreate();
setIsStrictModeForDevtools(false);
}
hook.memoizedState = [nextValue, nextDeps];
return nextValue;
}
解释:
在更新阶段,React 会判断 deps(依赖项数组) 是否发生变化,如果发生变化,React 将运行回调以获取新值并返回。如果没有改变,React 将返回旧值。
- 获取一个新的钩子对象:
hook = updateWorkInProgressHook(); - 如果 deps 数组为空
if (nextDeps !== null),则每次渲染时调用回调函数并返回新值。 3、如果deps数组不为空,则判断deps是否发生变化if (areHookInputsEqual(nextDeps, prevDeps))。如果没有改变,则返回旧值return prevState[0];。
3. 总结
- 在挂载阶段,调用mountMemo进行初始缓存,将传入的回调函数值和依赖数组传入
hook.memorizedState(这里可以看出和useCallback的不同,useCallback缓存的是函数) - 在更新阶段:调用updateMemo,判断依赖项数组是否发生变化,如果没有变化则从hook.memorizedState中取回原来的值返回,有变化则执行函数获取最新值返回。