人人都会AI编程

useMemo:缓存计算结果

更新时间:2026-07-10

useMemo 是一个 React Hook,用于缓存(记忆化)计算结果,避免在每次渲染时都重复执行昂贵的计算逻辑。它接受一个工厂函数和一个依赖项数组,只有当依赖项变化时,才会重新执行工厂函数并返回新的值;否则,直接返回上次缓存的结果。

基本语法

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
  • 第一个参数:返回需要缓存的计算结果的函数。注意它必须是一个纯函数,不应该有副作用。
  • 第二个参数:依赖项数组。当数组中的任何一个值发生变化时,才会重新执行工厂函数。

为什么需要 useMemo

React 的函数组件在状态或 Props 变化时会重新执行整个函数体。如果组件内部有复杂的数据处理(例如对长列表进行过滤、排序、聚合,或递归计算),这些计算会随每次渲染重复执行,即使结果可能完全一样,造成不必要的性能浪费。

useMemo 的本质是空间换时间:用内存空间存储上一次的结果,当依赖未变时直接复用,跳过耗时的计算过程。

使用场景与示例

示例1:避免昂贵的列表过滤

function ProductList({ products, category }) {
  // 只有当 products 或 category 变化时才重新过滤
  const filteredProducts = useMemo(() => {
    return products.filter(p => p.category === category);
  }, [products, category]);

  return (
    <ul>
      {filteredProducts.map(p => (
        <li key={p.id}>{p.name}</li>
      ))}
    </ul>
  );
}

如果没有 useMemo,每次父组件渲染造成 ProductList 重渲染时,即使 productscategory 不变,过滤逻辑也会重新执行。对于数百条数据可能没问题,但对于数千条或更复杂的过滤规则,就会产生可感知的性能影响。

示例2:依赖派生状态

当一个组件使用了 useStateuseReducer,并需要基于某些数据计算派生值时,useMemo 是理想的选择。

function Dashboard({ rawData }) {
  const statistics = useMemo(() => {
    // 假设这是一组耗时的聚合计算
    return {
      total: rawData.reduce((sum, item) => sum + item.amount, 0),
      average: rawData.reduce((sum, item) => sum + item.amount, 0) / rawData.length,
      max: Math.max(...rawData.map(item => item.amount)),
    };
  }, [rawData]);

  return (
    <div>
      <p>总计:{statistics.total}</p>
      <p>平均:{statistics.average}</p>
      <p>最高:{statistics.max}</p>
    </div>
  );
}

示例3:优化传递给子组件的引用类型 Props

当把对象或数组作为 Props 传递给使用 React.memo 包裹的子组件时,如果该引用在每次渲染时都重新创建,会导致子组件即使数据没变也会重渲染。useMemo 可以稳定这些引用。

function Parent({ items }) {
  // 避免每次渲染都创建新数组
  const sortedItems = useMemo(() => {
    return [...items].sort((a, b) => a.date - b.date);
  }, [items]);

  return <Child items={sortedItems} />;
}

const Child = React.memo(({ items }) => {
  // Child 只有在 sortedItems 引用变化时才重新渲染
  return <List data={items} />;
});

使用原则与注意事项

  1. 不要过早优化

useMemo 本身也有开销(存储和依赖比较)。对于简单的计算(如字符串拼接、简单的数学运算),使用 useMemo 可能得不偿失。只有当计算确实昂贵,且组件频繁渲染时才使用。

  1. 依赖项必须完整且正确

useMemo 的依赖数组应包含工厂函数内部使用到的所有响应式变量(state、props、其他 context 值等)。遗漏依赖会导致缓存失效和潜在的难以调试的 bug。推荐启用 eslint-plugin-react-hooksexhaustive-deps 规则,它会自动检测缺失的依赖项。

  1. 不要用于副作用

useMemo 只能包含纯计算,不能用于数据获取、手动操作 DOM 等副作用。这些应使用 useEffectuseLayoutEffect

  1. 不要依赖它来保证跨渲染的引用稳定性

虽然大部分情况下 useMemo 会返回相同的引用,但 React 可能在未来版本中在某些情况下丢弃缓存以释放内存。你的代码不应该强依赖这种缓存行为,而应将其视为性能优化手段。

  1. useCallback 的区别

useMemo 缓存的是计算结果(任何值),而 useCallbackuseMemo 的特化版本,专门用于缓存函数引用

   useCallback(fn, deps) 等价于 useMemo(() => fn, deps)
   

总结

useMemo 是 React 性能优化工具箱中的重要一环。在遇到渲染性能瓶颈时,通过缓存昂贵的计算结果和稳定引用类型 Props,可以有效减少不必要的重渲染和重复计算。但务必谨慎使用,优先测量性能问题,再精准施加优化。