简述
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
来存函数不是更妙吗?