React中useMemo和useCallback的作用:

一、useMemo 基本用法:

useMemo 是 React 提供的一个 Hook,用于性能优化,它通过"记忆"(memoization)计算结果来避免在每次渲染时进行不必要的复杂计算。

复制代码
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

主要作用:

避免重复计算:

当组件重新渲染时,useMemo 会检查依赖项数组中的值是否发生变化,只有当依赖项变化时,才会重新执行计算函数,否则直接返回之前缓存的值
2.

优化渲染性能:

对于计算量大的操作,使用 useMemo 可以显著提高性能,避免在每次渲染时都执行昂贵的计算
3.

保持引用稳定:

当返回对象或数组时,useMemo 可以保持引用不变(除非依赖项变化),这对于传递给子组件的 props 特别有用,可以避免不必要的子组件重新渲染

使用场景:

1. 复杂计算
复制代码
function ExpensiveComponent({ a, b }) {
  const result = useMemo(() => {
    // 复杂的计算过程
    return a * b + Math.pow(a, b) - a / b;
  }, [a, b]); // 只有当a或b变化时才重新计算

  return <div>{result}</div>;
}
2. 优化子组件渲染
复制代码
function ParentComponent({ items }) {
  const processedItems = useMemo(() => {
    return items.map(item => ({
      ...item,
      fullName: `${item.firstName} ${item.lastName}`
    }));
  }, [items]); // 只有当items变化时才重新处理

  return <ChildComponent items={processedItems} />;
}
3. 避免不必要的对象/数组创建
复制代码
function FormComponent({ onSubmit }) {
  const formConfig = useMemo(() => ({
    initialValues: { username: '', password: '' },
    validate: values => {
      const errors = {};
      if (!values.username) errors.username = 'Required';
      return errors;
    }
  }), []); // 空依赖数组表示只创建一次

  return <Form {...formConfig} onSubmit={onSubmit} />;
}

注意事项:

不要滥用:

useMemo 本身也有性能开销,只对真正昂贵的计算或需要稳定引用的值使用
2.

依赖数组:

确保包含所有在计算函数中使用的外部值,遗漏依赖项会导致 bug
3.

不是语义保证:

React 可能在将来选择"忘记"某些记忆化的值,不要依赖 useMemo 来实现业务逻辑的正确性
4.

与 React.memo 的区别:

useMemo 记忆的是值,React.memo 记忆的是组件

二、useCallback 基本用法:

主要作用:

useCallback 是 React 提供的用于性能优化 的 Hook,它的主要作用是缓存函数引用,避免不必要的函数重新创建。

使用场景:

1. 当函数作为依赖项时
复制代码
// 不使用 useCallback - 每次渲染都会创建新函数,导致 useEffect 重复执行
const fetchData = () => { /*...*/ };
useEffect(() => {
  fetchData();
}, [fetchData]); // 依赖项每次都会变化

// 使用 useCallback - 只有依赖变化时才创建新函数
const fetchData = useCallback(() => {
  /*...*/
}, [dependency]); // 只有当 dependency 变化时才重新创建函数
2. 当函数作为 props 传递给优化过的子组件时
复制代码
// 子组件用 React.memo 优化过
const Child = React.memo(({ onClick }) => {
  /*...*/
});

function Parent() {
  // 不使用 useCallback - 每次渲染都会导致 Child 重新渲染
  const handleClick = () => { /*...*/ };
  
  // 使用 useCallback - 避免 Child 不必要的重新渲染
  const handleClick = useCallback(() => {
    /*...*/
  }, []); // 空依赖表示函数永不变化
  
  return <Child onClick={handleClick} />;
}
3. 在自定义 Hook 中返回稳定函数时
复制代码
function useCounter() {
  const [count, setCount] = useState(0);
  
  // 返回稳定的函数引用
  const increment = useCallback(() => setCount(c => c + 1), []);
  const decrement = useCallback(() => setCount(c => c - 1), []);
  
  return { count, increment, decrement };
}

具体使用时机

  1. 函数被包含在依赖数组 (如 useEffect、useMemo、useCallback 的依赖数组)中时

  2. 函数作为 prop 传递给用 React.memo 优化的子组件时

  3. 函数被用作上下文值且会被多个组件使用时

  4. 函数被多次创建且创建成本较高时(虽然这种情况较少见)

不需要使用 useCallback 的情况

  1. 函数不参与任何依赖关系(没有作为 prop 传递,不在依赖数组中)
  2. 函数定义非常简单且创建成本极低
  3. 组件本身很少重新渲染时

三、useMemo与 **useCallback**的关系:

useCallback(fn, deps) 相当于 useMemo(() => fn, deps),两者都用于性能优化,但:

  1. useMemo 用于记忆计算结果

  2. useCallback 专门用于记忆函数

    ​// 这两个是等价的
    const memoizedCallback = useCallback(() => doSomething(a, b), [a, b]);
    const memoizedCallback = useMemo(() => () => doSomething(a, b), [a, b]);

相关推荐
@大迁世界1 天前
Firefox 本地模式:localhost 再见了 ?
前端·架构
米米商聊1 天前
同名附件为什么被 Map 覆盖?重复键与一对多索引
前端·javascript
echome8881 天前
Python 异常处理:try、else、finally 的正确搭配
开发语言·前端·python·异常处理·编程技巧
时间的情敌1 天前
OpenLayers 实战|Vue3 + OpenLayers 完整工程案例(加载天地图、要素、弹窗、绘制、WFS)
前端·openlayers
陈随易1 天前
下载神器!Motrix 停更三年后重写回归
前端·后端·程序员
前端的日常1 天前
一个人+AI做情侣食谱小程序,0 费用的服务器
前端·后端
计算机魔术师1 天前
谷歌搜索广告踩刹车,AI广告成为救命稻草
前端
总要学点什么1 天前
第81篇:数据可视化-PM2.5分布——让圆点的胖瘦替每座城市报出浓度
前端·信息可视化·echarts·cesium·空间分析
IT_陈寒1 天前
Vue的v-for里用了个临时变量,页面直接挂了
前端·人工智能·后端
hunter_0761 天前
一次 iOS 真机签名 409 的完整排查:证书、设备、描述文件为什么总是"对不上"
前端