React 18 如何使用 useMemo 避免不必要的重渲染?

文章导读
useMemo 主要用于缓存计算结果或稳定对象引用,但单独使用无法阻止子组件重渲染。必须配合 React.memo 包裹子组件,才能有效阻断因 props 引用变化导致的更新,适合计算密集型或深层组件树场景。
📋 目录
  1. 核心原理与常见误区
  2. 完整代码示例:父组件与子组件配合
  3. 验证步骤:使用 React DevTools Profiler
  4. 常见坑与排查
  5. 参考建议
A A

useMemo 主要用于缓存计算结果或稳定对象引用,但单独使用无法阻止子组件重渲染。必须配合 React.memo 包裹子组件,才能有效阻断因 props 引用变化导致的更新,适合计算密集型或深层组件树场景。

先说结论:useMemo 核心价值在于稳定引用和缓存计算,单独使用无法阻止渲染,需结合 React.memo 才能发挥优化作用。

  • 先定位:通过 React DevTools Profiler 确认哪些组件发生了不必要的重渲染。
  • 先做:对传递给子组件的对象、数组或函数使用 useMemo 或 useCallback 稳定引用。
  • 再验证:观察 Profiler 中组件渲染次数是否减少,且功能逻辑未受影响。

核心原理与常见误区

React 默认情况下,父组件重新渲染会触发所有子组件重新渲染。即使子组件 props 内容相同,如果传递的是内联对象或数组,每次渲染都会生成新引用。浅比较(Shallow Compare)会认为 props 已变化,从而触发重渲染。

React 18 如何使用 useMemo 避免不必要的重渲染?

误区:很多开发者只在父组件用了 useMemo 稳定了数据,却忘记给子组件包裹 React.memo。这种情况下,无论父组件数据引用是否稳定,子组件都会随父组件渲染而渲染。

React 18 如何使用 useMemo 避免不必要的重渲染?

完整代码示例:父组件与子组件配合

以下示例展示了如何正确配合使用。注意子组件 List 必须使用 React.memo 包裹,否则父组件的 useMemo 优化无效。

// ✅ 子组件:必须使用 React.memo 包裹
const List = React.memo(({ data }) => {
  console.log('List rendered');
  return (
    <ul>
      {data.map(item => <li key={item.id}>{item.name}</li>)}
    </ul>
  );
});

// ✅ 父组件:使用 useMemo 稳定 props 引用
const UserList = ({ users }) => {
  // 缓存计算结果,避免每次渲染生成新数组
  const filtered = useMemo(() => users.filter(u => u.active), [users]);
  
  return <List data={filtered} />;
};

验证步骤:使用 React DevTools Profiler

不要凭感觉判断优化是否生效,必须通过工具数据验证。

React 18 如何使用 useMemo 避免不必要的重渲染?
  1. 安装扩展:在浏览器安装 React Developer Tools 扩展。
  2. 开启 Profiler:打开浏览器的开发者工具,切换到 Profiler 面板。
  3. 录制交互:点击 Record 按钮,执行触发父组件更新的操作(如输入框变化、按钮点击),然后停止录制。
  4. 分析火焰图:查看组件渲染耗时。点击具体的组件条,查看右侧 "Why did this render?" 面板。
  5. 确认原因:如果优化成功,子组件不应出现在渲染列表中,或者显示 "Props did not change"。如果仍显示 "Props changed",检查引用是否真正稳定。

此外,可以在 React DevTools 设置中开启 "Highlight updates when components render",页面上重渲染的组件会闪烁绿框,直观观察更新范围。

常见坑与排查

  • 滥用 useMemo:简单计算(如加减法、简单字符串拼接)使用 useMemo 反而增加开销,记忆化本身也有成本。
  • 依赖项缺失:依赖数组不完整会导致缓存值过期,引发数据不同步。建议使用 eslint-plugin-react-hooks 辅助检查。
  • 忽略 React.memo:仅使用 useMemo 稳定 props 但子组件未包裹 React.memo,优化无效。
  • 对象深层变化:useMemo 和 React.memo 默认浅比较,深层嵌套对象属性变化可能无法察觉,需确保引用真正变化时才更新。
  • 排查步骤:若优化无效,先在子组件内加 console.log 确认是否执行,再检查父组件传递的 props 引用地址是否每次一致。

参考建议

  • React 官方文档:React.memo 与 useMemo API 说明
  • React 性能优化实战:如何避免不必要的重新渲染
  • React DevTools 官方使用指南