📘 本系列将系统解析 React 中的常用 Hooks,帮助你深入理解背后的原理与使用场景。本篇聚焦于两个性能优化利器:
useCallback
和useMemo
。
✨ 为什么需要它们?
React 在组件每次重新渲染时,都会重新执行函数体:
-
每个函数、对象、数组都重新创建新引用
-
会导致子组件重复渲染
-
会造成重复的复杂计算
我们可以用 useCallback
和 useMemo
来优化这些性能问题。
🔁 useCallback:缓存函数引用
✅ 基本用法
tsx
const memoizedFn = useCallback(() => {
// 逻辑...
}, [依赖项]);
每当依赖项不变时,返回的函数引用保持不变。
✅ 使用场景
-
将事件函数传递给
React.memo
包裹的子组件,避免不必要的重新渲染 -
减少事件绑定的频率(例如鼠标滚动、点击)
✅ 示例
tsx
const handleClick = useCallback(() => {
console.log('clicked');
}, []);
对比:
tsx
const handleClick = () => {
console.log('clicked');
};
不使用 useCallback
,每次组件渲染都会新建一个 handleClick
函数,导致引用变化。
🧠 useMemo:缓存计算值
✅ 基本用法
tsx
const memoizedValue = useMemo(() => {
return expensiveComputation(input);
}, [input]);
只有当依赖 input
变化时才会重新执行 expensiveComputation
。
✅ 使用场景
-
缓存复杂计算结果
-
避免创建新的对象、数组,防止触发子组件更新
-
数据筛选、排序等逻辑
✅ 示例
tsx
const filteredList = useMemo(() => {
return list.filter(item => item.includes(keyword));
}, [list, keyword]);
🔄 与 React.memo 联动优化子组件渲染
tsx
const Child = React.memo(({ onClick }: { onClick: () => void }) => {
console.log('子组件渲染');
return <button onClick={onClick}>Click</button>;
});
tsx
const handleClick = useCallback(() => {
console.log('clicked');
}, []);
<Child onClick={handleClick} />
如果不使用 useCallback
,每次父组件重渲染都会导致 onClick
引用变化,从而让 Child
也重新渲染。
📌 useCallback vs useMemo 对比
特性 | useCallback |
useMemo |
---|---|---|
返回值 | 函数 | 任何计算后的值 |
用途 | 缓存函数引用,避免重复定义 | 缓存计算结果,避免重复运算 |
常见使用场景 | 向子组件传递函数 | 缓存数组、对象、复杂计算 |
本质 | useMemo(() => fn, deps) |
useMemo(() => value, deps) |
🧷 注意事项
-
只有在真正的性能瓶颈或子组件频繁渲染时再使用
-
滥用可能引入复杂性
-
依赖数组必须写全,防止缓存失效或 bug
✅ 小结
Hook | 优化对象 | 典型场景 |
---|---|---|
useCallback |
函数 | 事件绑定、传递函数 props |
useMemo |
值 | 复杂计算、缓存数组/对象 |
📚 下一篇我们将解析:useEffect
的执行时机与副作用陷阱。