useMemo的10大使用场景

useMemo 是 React Hooks 中用于性能优化的核心 API,它的核心作用是缓存计算结果,避免在每次渲染时重复执行昂贵的计算 。以下是 useMemo 的详细应用场景、代码示例及最佳实践:


一、useMemo 的核心机制

  • 输入:一个函数(计算逻辑)和一个依赖项数组。
  • 输出:当依赖项变化时,重新执行函数并返回新值;否则返回缓存值。
  • 适用条件:当计算成本较高或需要稳定引用时使用。
jsx 复制代码
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

二、useMemo 的 10 个典型应用场景

1. 复杂计算缓存

场景 :组件内部有耗时计算(如大数据过滤、排序、数学运算)。
示例:计算斐波那契数列

jsx 复制代码
const fibonacci = (n) => {
  if (n <= 1) return n;
  return fibonacci(n - 1) + fibonacci(n - 2);
};

const Component = ({ n }) => {
  // 未优化:每次渲染都重新计算(性能极差)
  // const result = fibonacci(n); 

  // 优化后:仅当 n 变化时计算
  const result = useMemo(() => fibonacci(n), [n]);
  return <div>{result}</div>;
};

2. 避免子组件不必要的渲染

场景 :父组件传递引用类型(对象、数组、函数)给子组件时,避免因引用变化导致子组件重新渲染。
示例:传递配置对象

jsx 复制代码
const Child = React.memo(({ config }) => { /* ... */ });

const Parent = () => {
  const [count, setCount] = useState(0);

  // 未优化:每次渲染生成新对象,导致 Child 重新渲染
  // const config = { color: "red", size: 10 };

  // 优化后:仅当依赖项变化时生成新对象
  const config = useMemo(() => ({ color: "red", size: 10 }), []);

  return (
    <div>
      <button onClick={() => setCount(c => c + 1)}>Update Parent</button>
      <Child config={config} />
    </div>
  );
};

3. 优化 Context 传递的值

场景 :当 Context 提供的数据是动态生成的引用类型时,避免消费者组件因引用变化而重新渲染。
示例:动态主题配置

jsx 复制代码
const ThemeContext = createContext();

const ThemeProvider = ({ children }) => {
  const [darkMode, setDarkMode] = useState(false);

  // 未优化:每次渲染生成新对象,导致所有消费者重新渲染
  // const theme = { darkMode, colors: darkMode ? darkColors : lightColors };

  // 优化后:仅当 darkMode 变化时生成新对象
  const theme = useMemo(() => ({
    darkMode,
    colors: darkMode ? darkColors : lightColors
  }), [darkMode]);

  return (
    <ThemeContext.Provider value={theme}>
      {children}
    </ThemeContext.Provider>
  );
};

4. 格式化或数据处理

场景 :数据转换(如日期格式化、国际化、数据聚合)。
示例:日期格式化

jsx 复制代码
const formatDate = (date) => {
  // 复杂的格式化逻辑
  return new Intl.DateTimeFormat('en-US').format(date);
};

const Component = ({ timestamp }) => {
  // 未优化:每次渲染都格式化
  // const formattedDate = formatDate(timestamp);

  // 优化后:仅当 timestamp 变化时格式化
  const formattedDate = useMemo(() => formatDate(timestamp), [timestamp]);
  return <div>{formattedDate}</div>;
};

5. 动态样式生成

场景 :根据状态生成复杂 CSS 样式对象。
示例:动态样式计算

jsx 复制代码
const Component = ({ isActive }) => {
  // 未优化:每次渲染生成新样式对象
  // const style = { 
  //   color: isActive ? 'red' : 'black',
  //   transform: `scale(${isActive ? 1.2 : 1})`
  // };

  // 优化后:仅当 isActive 变化时生成新对象
  const style = useMemo(() => ({
    color: isActive ? 'red' : 'black',
    transform: `scale(${isActive ? 1.2 : 1})`
  }), [isActive]);

  return <div style={style}>Dynamic Style</div>;
};

6. 大型列表或表格中的行渲染

场景 :渲染包含复杂行数据的列表时,避免每次渲染重新计算行数据。
示例:表格行数据预处理

jsx 复制代码
const Table = ({ data, sortKey }) => {
  // 未优化:每次渲染都重新排序
  // const sortedData = data.sort((a, b) => a[sortKey] - b[sortKey]);

  // 优化后:仅当 data 或 sortKey 变化时排序
  const sortedData = useMemo(() => 
    [...data].sort((a, b) => a[sortKey] - b[sortKey]), 
    [data, sortKey]
  );

  return (
    <table>
      {sortedData.map(row => <TableRow key={row.id} data={row} />)}
    </table>
  );
};

7. 避免重复渲染中的副作用

场景 :当某个值需要传递给副作用(如 useEffect)且需要稳定引用时。
示例:依赖稳定引用的副作用

jsx 复制代码
const Component = ({ id }) => {
  // 未优化:每次渲染生成新配置对象,导致 useEffect 重复执行
  // const config = { id, type: "FETCH" };

  // 优化后:仅当 id 变化时生成新配置
  const config = useMemo(() => ({ id, type: "FETCH" }), [id]);

  useEffect(() => {
    fetchData(config);
  }, [config]);

  return <div>...</div>;
};

8. 动态导入组件(Code Splitting)

场景 :按需加载组件时,避免重复动态导入。
示例:动态加载弹窗组件

jsx 复制代码
const LazyModal = React.lazy(() => import('./Modal'));

const Component = ({ showModal }) => {
  // 未优化:每次渲染都重新生成 JSX
  // const modal = showModal ? <LazyModal /> : null;

  // 优化后:仅在 showModal 变化时重新渲染
  const modal = useMemo(() => 
    showModal ? <LazyModal /> : null, 
    [showModal]
  );

  return <div>{modal}</div>;
};

9. 避免闭包中的过时值

场景 :在回调函数中需要访问最新状态时,结合 useRef 使用。
示例:定时器中的最新状态

jsx 复制代码
const Component = () => {
  const [count, setCount] = useState(0);
  const countRef = useRef(count);

  // 使用 useMemo 缓存回调函数,避免闭包陷阱
  const updateCount = useMemo(() => () => {
    console.log("Current count:", countRef.current);
  }, []);

  useEffect(() => {
    countRef.current = count; // 同步最新值到 ref
  }, [count]);

  useEffect(() => {
    const timer = setInterval(updateCount, 1000);
    return () => clearInterval(timer);
  }, [updateCount]);

  return <button onClick={() => setCount(c => c + 1)}>Increment</button>;
};

10. 优化自定义 Hook 的输出

场景 :自定义 Hook 返回复杂数据时,避免调用组件不必要的更新。
示例:自定义数据获取 Hook

jsx 复制代码
const useFetchData = (url) => {
  const [data, setData] = useState(null);

  useEffect(() => {
    fetch(url).then(res => res.json()).then(setData);
  }, [url]);

  // 未优化:每次返回新对象,导致使用该 Hook 的组件重新渲染
  // return { data, isLoading: !data };

  // 优化后:稳定返回值引用
  return useMemo(() => ({ data, isLoading: !data }), [data]);
};

三、useMemo 的误用场景

1. 简单计算

jsx 复制代码
// ❌ 误用:计算过于简单,反而增加内存开销
const sum = useMemo(() => a + b, [a, b]);

// ✅ 正确:直接计算
const sum = a + b;

2. 依赖项不完整

jsx 复制代码
// ❌ 误用:依赖项缺失,导致缓存值过时
const result = useMemo(() => a + b, [a]);

// ✅ 正确:包含所有依赖项
const result = useMemo(() => a + b, [a, b]);

3. 滥用引用稳定

jsx 复制代码
// ❌ 误用:即使不需要稳定引用,也强制缓存
const handleClick = useMemo(() => () => { /* ... */ }, []);

// ✅ 正确:优先使用 useCallback
const handleClick = useCallback(() => { /* ... */ }, []);

四、最佳实践总结

  1. 按需优化 :先用 Profiler 定位性能瓶颈,再针对性使用 useMemo
  2. 权衡成本:计算成本 < 缓存开销时,避免使用。
  3. 依赖项精确:确保依赖项数组包含所有变化的变量。
  4. 避免嵌套滥用 :过度使用 useMemo 可能导致代码可读性下降。

通过合理应用 useMemo,可以有效减少不必要的计算和渲染,提升 React 应用性能。核心原则是:在计算成本高或需要稳定引用时使用,避免在简单场景中滥用

相关推荐
z_mazin38 分钟前
Chrome开发者工具实战:调试三剑客
前端·javascript·chrome·网络爬虫
sen_shan2 小时前
Vue3+Vite+TypeScript+Element Plus开发-04.静态菜单设计
前端·javascript·typescript·vue3·element·element plus·vue 动态菜单
旧识君2 小时前
移动端1px终极解决方案:Sass混合宏工程化实践
开发语言·前端·javascript·前端框架·less·sass·scss
吃没吃3 小时前
vue2.6-源码学习-Vue 核心入口文件分析
前端
Carlos_sam3 小时前
Openlayers:海量图形渲染之图片渲染
前端·javascript
XH2763 小时前
Android Retrofit用法详解
前端
鸭梨大大大3 小时前
Spring Web MVC入门
前端·spring·mvc
吃没吃3 小时前
vue2.6-源码学习-Vue 初始化流程分析 (src/core/instance/init.js)
前端
XH2763 小时前
Android Room用法详解
前端
木木黄木木4 小时前
css炫酷的3D水波纹文字效果实现详解
前端·css·3d