今天我们要聊一个听起来“高大上”,其实特别实用的 Hook —— useCallback。
useCallback 是干嘛的?
简单说:记住一个函数,别让它每次组件重新渲染时都“变脸”。在 JS 中,函数是“引用类型”,每次函数组件执行(也就是重新渲染),函数定义也会重新生成。即使你写的是一样的函数,它也是一个“新函数”。
useCallback 的目的:如果依赖没变,就不要重新生成这个函数。
举个例子:没用 useCallback 时的尴尬
想象下面这个场景
你有一个父组件,传了个 handleClick 函数给子组件。子组件用 React.memo 包了起来,想让它只在 props 变了的时候重新渲染。结果你每次渲染都传了一个“新函数”,子组件每次都重新渲染 —— 优化白做。
这时候就要用 useCallback :
const handleClick = useCallback(() => {
console.log('Clicked!');
}, []);
空依赖数组代表:这个函数只会在初次渲染时生成一次,之后每次渲染都复用它。
useCallback 的语法和依赖数组
const memoizedFn = useCallback(() => {
doSomething(a, b);
}, [a, b]);
第一个参数是你要“记忆”的函数体
第二个参数是“依赖项数组”
当依赖项 a 或 b 改变时,这个函数才会被重新生成
备注:和 useMemo 很像,但 useMemo 是记住“值”,useCallback 是记住“函数”。
核心场景:避免不必要的子组件重渲染
搭配 React.memo,useCallback 的作用才最明显:
const Parent = () => {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => {
console.log('Clicked!');
}, []);
return <Child onClick={handleClick} />;
};
const Child = React.memo(({ onClick }) => {
return <button onClick={onClick}>Click me</button>;
});
✅ 如果不用 useCallback,每次渲染 handleClick 都变,Child 也重渲染。
✅ 如果用了 useCallback,函数是稳定引用,Child 就不会白白更新。
useCallback vs useMemo 有啥不同?
特性
|
useMemo
|
useCallback
|
记忆的对象
|
一个值/计算结果
|
一个函数引用
|
使用场景
|
缓存昂贵计算
|
缓存事件处理函数/回调
|
返回值
|
值
|
函数
|
小贴士:用 useCallback 相当于 useMemo(() => fn, deps) 的语法糖。
综合实战挑战:构建一个多 Hook 的组件!
任务:写一个「可加载数据的计数器组件」
用 useState 管理 count 和 loading 状态
用 useEffect 模拟数据加载
用 useRef 聚焦按钮
用 useMemo 计算派生数据(例如是否是偶数)
用 useCallback 创建事件处理函数(比如按钮点击)
尝试任意一种样式方法(Tailwind / CSS Module / Emotion)
总结时间:useCallback 什么时候用?
适合:
1.函数作为 prop 传给子组件
2.子组件使用了 React.memo
3.你确实遇到了性能问题 or 不必要的重渲染
不适合:
1.函数本身很轻量,不会影响性能
2.没有作为 prop 传递的需求
记住一句话:优化是有成本的,用在对的场景才有价值。