简述
useMemo和useCallback相对来说源码比较简单,在函数组件执行到对应的Hook时,同样会将包含该Hook信息的对象链接到Fiber节点的memoizedState属性上的Hooks链表。
useMemo的Hook对象的memoizedState属性上存的值为计算后的值和依赖数组 —— hook.memoizedState = [nextValue, nextDeps]。
useCallback的Hook 对象的memoizedState属性上存的值为回调函数和依赖数组 —— hook.memoizedState = [callback, nextDeps]。
以下源码浅析React版本为17.0.1。
useMemo
在React中,Hooks在Mount时和Update时使用的是两个不同函数(useContext除外)。
Mount时
function mountMemo<T>(
nextCreate: () => T,
deps: Array<mixed> | void | null,
): T {
// 添加到Fiber节点上的Hooks链表
const hook = mountWorkInProgressHook();
const nextDeps = deps === undefined ? null : deps;
// 计算需要memo的值
const nextValue = nextCreate();
// hook数据对象上存的值
hook.memoizedState = [nextValue, nextDeps];
return nextValue;
}Update时
function updateMemo<T>(
nextCreate: () => T,
deps: Array<mixed> | void | null,
): T {
// 找到该useMemo对应的hook数据对象
const hook = updateWorkInProgressHook();
const nextDeps = deps === undefined ? null : deps;
// 之前存的[nextValue, nextDeps]
const prevState = hook.memoizedState;
if (prevState !== null) {
if (nextDeps !== null) {
const prevDeps: Array<mixed> | null = prevState[1];
// 判断依赖是否相等
if (areHookInputsEqual(nextDeps, prevDeps)) {
// 相等就返回上次的值
return prevState[0];
}
}
}
// 不相等重新计算
const nextValue = nextCreate();
hook.memoizedState = [nextValue, nextDeps];
return nextValue;
}useCallback
之前有看别人讲useCallback是useMemo的语法糖,现在一看,虽然两个方法完全不同,但也基本完全相同了。
Mount时
function mountCallback<T>(callback: T, deps: Array<mixed> | void | null): T {
// 添加到Fiber节点上的Hooks链表
const hook = mountWorkInProgressHook();
const nextDeps = deps === undefined ? null : deps;
// memoizedState存的值是callback
hook.memoizedState = [callback, nextDeps];
return callback;
}Update时
function updateCallback<T>(callback: T, deps: Array<mixed> | void | null): T {
// 找到该useMemo对应的hook数据对象
const hook = updateWorkInProgressHook();
const nextDeps = deps === undefined ? null : deps;
const prevState = hook.memoizedState;
if (prevState !== null) {
if (nextDeps !== null) {
const prevDeps: Array<mixed> | null = prevState[1];
if (areHookInputsEqual(nextDeps, prevDeps)) {
return prevState[0];
}
}
}
hook.memoizedState = [callback, nextDeps];
return callback;
}这俩方法的源码也太短了,这样就水了一文。
其实一直有一个疑问,如果真的要想让一个函数的地址不发生变化,用useRef来存函数不是更妙吗?