Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

hook:useMemo

  • 用来记忆并缓存,在指定依赖没有更改时,直接返回缓存值,无需重新计算,可用于优化性能

场景代码示例

  • 【场景 1】每次渲染都重新计算(不建议使用):

    • 每次组件更新后,都会重新计算值,失去了 useMemo 的意义

      // 场景:计算属性 - 每次渲染都计算(无意义)
      const memoizedValue = useMemo(() => computeExpensiveValue(a, b));
      // 这里没有依赖项
      
    • 这样写等同于直接调用 computeExpensiveValue(a, b),没有缓存效果

    • ⚠️ 不建议使用,useMemo 的核心价值就是缓存,没有依赖项就失去了意义

  • 【场景 2】依赖变化时重新计算(常用):

    • 依赖项变化时才重新计算,否则返回缓存值

      // 场景:昂贵计算 - 仅 a 或 b 变化时重新计算
      const memoizedValue = useMemo(() => {
        return computeExpensiveValue(a, b);
      }, [a, b]); // 依赖 a 和 b
      
    • 只有当 ab 发生变化时,才会重新执行计算函数

    • 其他状态变化导致组件重新渲染时,直接返回缓存的 memoizedValue

  • 【场景 3】仅首次渲染计算(少见):

    • 只在首次渲染时计算一次,后续永远返回缓存值

      // 场景:初始化配置 - 只计算一次
      const config = useMemo(() => {
        return generateConfig();
      }, []); // 空依赖数组
      
    • 适用于一次性计算的场景,比如生成初始配置

    • 注意:如果真的不需要响应任何变化,直接在组件外定义常量可能更好

  • 【场景 4】缓存对象/数组引用(常用):

    • 避免每次渲染都创建新的对象/数组,导致子组件不必要的重渲染

      // 场景:缓存对象引用 - 避免子组件无意义渲染
      const user = useMemo(() => {
        return { name, age };
      }, [name, age]); // 只有 name 或 age 变化时才创建新对象
      
      return <UserProfile user={user} />; // user 引用稳定,子组件不会频繁渲染
      
    • 如果不用 useMemo,每次渲染都会创建新的 { name, age } 对象

    • 即使 name 和 age 值没变,新对象的引用也不同,导致子组件重新渲染

  • 【场景 5】缓存 JSX 元素(常用):

    • 防止子组件不必要的重新渲染

      const [count, setCount] = useState(0);
      const [time, setTime] = useState(0);
      
      // child1 只在 count 变化时重新创建
      const child1 = useMemo(() => <Counter count={count} />, [count]);
      // child2 只在 time 变化时重新创建
      const child2 = useMemo(() => <Time time={time} />, [time]);
      
      return (
        <div>
          <button onClick={() => setCount(count + 1)}>count + 1</button>
          <button onClick={() => setTime(time + 1)}>time + 1</button>
          {child1} {/* count 变化时才重新渲染 */}
          {child2} {/* time 变化时才重新渲染 */}
        </div>
      );
      
    • 点击 count 按钮时,child2 不会重新渲染

    • 点击 time 按钮时,child1 不会重新渲染

    • 实现了类似 shouldComponentUpdate 的效果


依赖数组的区别

场景依赖数组计算时机使用场景
场景 1每次渲染都计算⚠️ 不建议
场景 2[dep]首次 或 依赖变化常用(计算属性)
场景 3[]仅首次渲染少见(一次性计算)
场景 4[dep]首次 或 依赖变化常用(缓存对象引用)
场景 5[dep]首次 或 依赖变化常用(缓存 JSX)

注意事项

  • useMemo 是性能优化手段,不要过度使用,简单计算直接写就好
  • useMemo 的计算函数应该是纯函数,不应该有副作用
  • 不要依赖 useMemo 来阻止渲染,React 不保证缓存永久有效