前言
useCallback
和useMemo
都是Hook中用于性能优化的;
useCallback
是一个允许你在多次渲染中缓存函数的 React Hook。
源码解析
js
const cachedFn = useCallback(fn, dependencies)
mount阶段
按照常规流程,调用HooksDispatcherOnMountInDEV.useCallback 的方法,前置check检查方法,核心方法mountCallback ;
首先调用mountWorkInProgressHook方法,
js
const hook = {
memoizedState: null,
baseState: null,
baseQueue: null,
queue: null,
next: null
};
fiber.memoizedState = hook;
mountCallback核心代码就是将fn和dependencies保存下来,并返回fn;
js
hook.memoizedState = [fn, dependencies];
return fn;
useCallback初始化阶段就是创建一个hook对象,然后将依赖和函数保存下来;
update阶段
调用HooksDispatcherOnUpdateInDEV.useCallback 方法,调用check方法,核心方法updateCallback ;
调用updateWorkInProgressHook 阶段,就是更新hook对象;
将hook.memoizedState获取的值和当前传入的值,Object.is(hook.memoizedState[1],dependencies),
如果是一样的,就返回hook.memoizedState[0],否则
js
hook.memoizedState = [fn, dependencies];
return fn;
总结
useCallback源码很简单,就是将函数保存下来,根据前后两次dependencies,如果没变的话就是返回上次函数,否则就会返回当前函数。
补充
useCallback
并不会导致组件更新,而用户直接使用useCallback
减少不必要的函数创建,这并不是很好的优化方式,
我理解的有一种适合的场景,防止子组件的不必要渲染,如果将回调函数传递给 React.memo
包裹的子组件,使用 useCallback
可以确保即使父组件重新渲染,子组件也不会因为新的回调函数而重新渲染。
js
const MemoizedChildComponent = React.memo(({ onClick }) => {
// 子组件的渲染逻辑
return <button onClick={onClick}>Click me</button>;
});
const ParentComponent = () => {
const handleClick = useCallback(() => {
console.log('Button clicked!');
}, []);
return <MemoizedChildComponent onClick={handleClick} />;
};
函数与依赖关系
无论是useCallback
或者useEffect
等有依赖项的hook,都是通过闭包实现的,举个简单例子
js
// mount
let cache
function component(){
let state = 'old'
function hook(){
console.log(state)
}
cache = hook
hook();
}
component()
// update
function component(){
let state = 'new'
function hook(){
console.log(state)
}
// if deps没变化 成立执行cache,否则执行hook
cache()
}
component()
这就相当于使用useCallback
的过程,第二次打印也是'old',