简单回顾下useMemo

一、useMemo 的作用和意义

useMemo 是 React 提供的一个 Hook,用于‌性能优化 ‌,它的主要作用是‌缓存计算结果‌,避免在每次渲染时都进行不必要的复杂计算。

核心意义:

  1. 性能优化‌:避免重复执行昂贵的计算
  2. 引用稳定性‌:保持对象/数组的引用不变,避免子组件不必要的重新渲染
  3. 按需计算‌:只在依赖项变化时才重新计算值

二、基本语法

scss 复制代码
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
  • 第一个参数:计算函数,返回要缓存的值
  • 第二个参数:依赖项数组,只有当依赖项变化时才会重新计算

三、实际应用场景

1. 复杂计算缓存

javascript 复制代码
function ExpensiveComponent({ list }) {
  const sortedList = useMemo(() => {
    console.log('重新排序...');
    return [...list].sort((a, b) => a.value - b.value);
  }, [list]);

  return <div>{sortedList.map(item => <div key={item.id}>{item.value}</div>)}</div>;
}

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

javascript 复制代码
jsxCopy Code
function Parent({ a, b }) {
  const childProps = useMemo(() => ({ a, b }), [a, b]);
  
  return <Child {...childProps} />;
}

// 只有当a或b变化时,Child才会重新渲染
const Child = React.memo(({ a, b }) => {
  return <div>{a} - {b}</div>;
});

3. 大型列表或数据转换

javascript 复制代码
jsxCopy Code
function DataTable({ rawData }) {
  const processedData = useMemo(() => {
    return rawData.map(item => ({
      ...item,
      fullName: `${item.firstName} ${item.lastName}`,
      formattedDate: formatDate(item.timestamp)
    }));
  }, [rawData]);

  return <Table data={processedData} />;
}

4. 避免昂贵的初始化

typescript 复制代码
jsxCopy Code
function ComponentWithHeavyInit({ type }) {
  const config = useMemo(() => {
    return getExpensiveConfig(type);
  }, [type]);

  return <div>{config.title}</div>;
}

四、使用注意事项

  1. 不要滥用‌:不是所有计算都需要useMemo,简单计算可能比useMemo开销更大
  2. 依赖项要准确‌:确保所有在计算函数中使用的外部值都包含在依赖项中
  3. 避免副作用‌:计算函数应该是纯函数,不应包含副作用
  4. 与React.memo配合‌:useMemo缓存值,React.memo缓存组件
  5. 不是防抖/节流‌:useMemo不是用来控制函数执行频率的

五、高级面试题及答案

1. useMemo和useCallback有什么区别?

  • useMemo 用于缓存计算结果,返回的是计算得到的值
  • useCallback 用于缓存函数本身,返回的是函数引用
  • 本质上,useCallback(fn, deps) 等同于 useMemo(() => fn, deps)

2. 什么情况下不应该使用useMemo?

  • 计算非常简单,开销小于useMemo本身
  • 依赖项频繁变化,导致频繁重新计算
  • 组件很少重新渲染,优化效果不明显
  • 计算函数有副作用(应该使用useEffect)

3. useMemo能保证引用不变吗?

  • 在依赖项不变的情况下,useMemo会返回相同的引用
  • 但React可能会在内存压力大时释放缓存(理论上,实践中很少见)
  • 对于必须保持引用稳定的场景,可以考虑使用ref

4. 如何测试useMemo的效果?

  • 使用React DevTools的Profiler测量渲染性能
  • 添加console.log到计算函数中观察调用频率
  • 比较使用前后的组件渲染次数和耗时
  • 注意不要仅凭感觉判断优化效果

5. useMemo和React.memo如何配合使用?

  • React.memo 缓存组件,避免props未变时的重新渲染

  • useMemo 缓存props中的复杂值,确保引用稳定

  • 两者配合可以最大程度减少不必要的渲染:

    ini 复制代码
    const MemoizedChild = React.memo(Child);
    
    function Parent({ items }) {
      const processedItems = useMemo(() => processItems(items), [items]);
      return <MemoizedChild items={processedItems} />;
    }

1. ‌useMemo 的同步特性

  • useMemo 是同步执行的 Hook,设计初衷是用于同步计算和缓存值‌
  • 直接在 useMemo 中返回 Promise 会导致缓存的是 Promise 对象而非实际结果‌
  • 异步操作在 useMemo 中不会被立即执行,而是会被放入事件队列延迟处理‌

2. ‌常见错误用法示例

scss 复制代码
// 错误用法1:直接返回Promise
const data = useMemo(() => fetchData(), []); // 缓存的是Promise对象‌

// 错误用法2:使用async/await语法
const data = useMemo(async () => {
  return await fetchData(); // 同样返回Promise‌

3. ‌替代方案

对于需要缓存异步数据的场景,推荐以下解决方案:

方案A:useState + useEffect 组合

scss 复制代码
const [data, setData] = useState(null);
useEffect(() => {
  fetchData().then(setData);
}, [deps]);

方案B:自定义 useAsyncMemo Hook

scss 复制代码
function useAsyncMemo(factory, deps) {
  const [value, setValue] = useState(null);
  useEffect(() => {
    factory().then(setValue);
  }, deps);
  return value;
}
// 使用示例‌
const data = useAsyncMemo(() => fetchData(), [deps]);

方案C:使用第三方库

use-async-memo 库专门处理异步记忆化场景‌1

相关推荐
全栈前端老曹5 小时前
【前端地图】多地图平台适配方案——高德、百度、腾讯、Google Maps SDK 差异对比、封装统一地图接口
前端·javascript·百度·dubbo·wgs84·gcj-02·bd09
笑虾5 小时前
Win10 修改注册表 让鼠标悬停PNG上时 tip 始终显示分辨率
开发语言·javascript·ecmascript
禧西5 小时前
面试准备——agent和大模型
面试·职场和发展
雾岛听风6916 小时前
JavaScript基础语法速查手册
开发语言·前端·javascript
遇见~未来6 小时前
第三篇_现代布局_从弹性到网格
前端·css3
前端那点事6 小时前
Vue前端SEO优化全攻略(实操落地版,新手也能上手)
前端·vue.js
Dxy12393102166 小时前
HTML 如何使用 SVG 画曲线
前端·算法·html
用户2367829801686 小时前
从零实现 GIF 制作工具:LZW 压缩与 Median Cut 色彩量化
前端·javascript
hahaha 1hhh6 小时前
中文乱码 ubuntu autodl
linux·运维·前端
棉猴6 小时前
Python海龟绘图之绘制文本
javascript·python·html·write·turtle·海龟绘图·输出文本