字数
1042 字
阅读时间
5 分钟
useMemo 和 useCallback 都是 React 中用于缓存计算结果的 Hooks,目的是减少不必要的重复计算或组件重渲染,提升性能。但它们的缓存对象和使用场景有明确区别:
核心区别:缓存的对象不同
useMemo:缓存计算结果(值),比如复杂运算的返回值、对象、数组等。useCallback:缓存函数本身,避免函数在每次渲染时被重新创建。
具体对比与场景
1. useMemo:缓存“值”,避免重复计算
当组件中存在耗时的计算逻辑(如大数据处理、复杂运算),或需要避免引用类型(对象/数组)被频繁创建导致子组件不必要重渲染时,使用 useMemo。
语法:
jsx
const memoizedValue = useMemo(() => {
// 执行计算并返回结果(值)
return 复杂计算的结果;
}, [依赖项数组]); // 依赖项变化时,重新计算并缓存新值示例:
假设有一个计算“数组元素总和”的耗时操作,且依赖于 list 变化:
jsx
function MyComponent({ list }) {
// 用 useMemo 缓存计算结果,只有 list 变化时才重新计算
const total = useMemo(() => {
console.log('重新计算总和');
return list.reduce((sum, item) => sum + item, 0);
}, [list]); // 依赖 list
return <div>总和:{total}</div>;
}- 如果不用
useMemo,每次组件渲染时都会执行reduce计算,即使list没变。 - 用
useMemo后,只有list变化时才会重新计算,否则直接复用缓存的total。
2. useCallback:缓存“函数”,避免子组件无效重渲染
当需要将一个函数传递给子组件(尤其是使用 React.memo 优化的子组件)时,useCallback 可以缓存函数引用,避免因“函数每次渲染被重新创建”导致子组件不必要的重渲染。
语法:
jsx
const memoizedCallback = useCallback(() => {
// 函数逻辑
}, [依赖项数组]); // 依赖项变化时,重新创建函数并缓存示例:
子组件 Child 用 React.memo 包裹(仅在 props 变化时重渲染),父组件传递一个回调函数:
jsx
// 子组件:用 React.memo 优化,避免无效重渲染
const Child = React.memo(({ onHandle }) => {
console.log('子组件渲染了');
return <button onClick={onHandle}>点击</button>;
});
// 父组件
function Parent() {
const [count, setCount] = useState(0);
// 用 useCallback 缓存函数,只有依赖变化时才重新创建
const handleClick = useCallback(() => {
console.log('点击事件');
}, []); // 无依赖,函数始终是同一个引用
return (
<div>
<p>计数:{count}</p>
<button onClick={() => setCount(count + 1)}>加1</button>
<Child onHandle={handleClick} />
</div>
);
}- 如果不用
useCallback,父组件每次渲染时,handleClick都会被重新创建(函数引用变化),导致Child组件即使被React.memo包裹也会重新渲染。 - 用
useCallback后,handleClick引用不变(依赖为空数组),父组件渲染时Child不会因函数引用变化而重渲染。
关键总结
| 特性 | useMemo | useCallback |
|---|---|---|
| 缓存对象 | 计算结果(值、对象、数组等) | 函数本身 |
| 本质 | useCallback(fn, deps) 等价于 useMemo(() => fn, deps) | - |
| 主要用途 | 避免重复计算耗时操作 | 避免子组件因函数引用变化而重渲染 |
| 依赖变化时 | 重新计算并缓存新值 | 重新创建函数并缓存新引用 |
注意事项
- 不要过度使用:缓存本身有成本(内存占用),对于简单计算或不传递给子组件的函数,无需缓存。
- 依赖项必须正确:如果依赖项变化但未加入依赖数组,会导致缓存的结果/函数过时,引发 bug。
useCallback通常配合React.memo使用,否则意义不大(子组件本身会频繁重渲染时,缓存函数没用)。