理解useMemo
与useCallback
的实现非常容易,我们以mount
与update
两种情况分别讨论这两个hook
mount
function mountMemo<T>(
nextCreate: () => T,
deps: Array<mixed> | void | null,
): T {
const hook = mountWorkInProgressHook();
const nextDeps = deps === undefined ? null : deps;
const nextValue = nextCreate();
hook.memoizedState = [nextValue, nextDeps];
return nextValue;
}
function mountCallback<T>(callback: T, deps: Array<mixed> | void | null): T {
const hook = mountWorkInProgressHook();
const nextDeps = deps === undefined ? null : deps;
hook.memoizedState = [callback, nextDeps];
return callback;
}
mountMemo
会将回调函数
(nextCreate)的执行结果作为value
保存
mountCallback
会将回调函数
作为value
保存
update
function updateMemo<T>(
nextCreate: () => T,
deps: Array<mixed> | void | null,
): T {
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];
}
}
}
const nextValue = nextCreate();
hook.memoizedState = [nextValue, nextDeps];
return nextValue;
}
function updateCallback<T>(callback: T, deps: Array<mixed> | void | null): T {
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;
}
- 对于
update
,这两个hook
的唯一区别也是回调函数本身还是回调函数的执行结果作为value
最终我们可以得知useCallback
在deps
没有发生变化时,新声明的callback
,没有使用,所以useCallback
只是对减少子组件渲染起作用,自身组件使用反而是反向优化
最后补充一个以render
后的代价换取render
时的性能一个自定义hooks
function useCallbackRef<T extends (...args: any[]) => any>(callback: T | undefined): T {
const callbackRef = React.useRef(callback);
React.useEffect(() => {
callbackRef.current = callback;
});
return React.useMemo(() => ((...args) => callbackRef.current?.(...args)) as T, []);
}