useMemo 和 useCallback 有什么区别?

useMemouseCallback 都是 React 的优化 Hooks,它们的主要目的是提高性能和避免不必要的计算或函数创建。那么它们又有哪些不同点呢?今天就让我们一起来讨论一下吧!

useMemo

useMemo 通过缓存计算结果来避免在每次渲染时都进行昂贵的计算。当你有一个计算或者对象构造函数,其结果依赖于某些输入(依赖项),你可以使用 useMemo 来存储这个结果。当依赖项没有改变时,React 将重用之前计算的结果,而不是重新计算。

工作原理:

  1. 在组件 render 期间,useMemo 接收一个创建函数和一个依赖项数组作为参数。
  2. 如果依赖项数组中的值与上一次 render 时的值相比没有任何变化,useMemo 就会返回上次计算的结果。
  3. 如果依赖项中有任何值发生了变化,useMemo 就会重新执行创建函数,并将新的结果缓存起来,然后返回这个新结果。

TypeScript 代码实例:

typescript 复制代码
import { useMemo } from 'react';

interface Props {
  array: number[];
}

function ComplexCalculation({ array }: Props) {
  // 使用 useMemo 缓存排序后的数组
  const sortedArray = useMemo(() => {
    // 这是一个复杂的计算或者对象构造函数
    return array.sort((a, b) => a - b);
  }, [array]);

  // 当 array 变化时,sortedArray 才会重新计算
  console.log('Sorted Array:', sortedArray);

  return (
    <div>
      {sortedArray.map(num => (
        <p key={num}>{num}</p>
      ))}
    </div>
  );
}

在这个例子中,我们有一个 ComplexCalculation 组件,它接收一个数字数组作为属性。我们使用 useMemo 来缓存排序后的数组。只有当 array 属性发生变化时,才会重新计算 sortedArray

这样做的好处是,如果 array 在多次渲染中保持不变,我们就无需每次都对数组进行排序,从而提高了性能。同时,由于 sortedArray 是在 useMemo 中计算的,所以它不会影响到 React 的渲染过程,保证了组件的状态管理。

useCallback

useCallback 主要用于缓存函数。当你有一个需要在 render 或者事件处理等场景中使用的函数,并且你希望在依赖项没有改变时保持该函数的引用不变,可以使用 useCallback

以下是一个详细的 useCallback 工作原理和 TypeScript 示例:

工作原理:

  1. 在组件 render 期间,useCallback 接收一个创建函数和一个依赖项数组作为参数。
  2. 如果依赖项数组中的值与上一次 render 时的值相比没有任何变化,useCallback 就会返回上次创建的函数引用。
  3. 如果依赖项中有任何值发生了变化,useCallback 就会重新执行创建函数,并将新的函数引用缓存起来,然后返回这个新引用。

TypeScript 代码实例:

typescript 复制代码
import { useCallback } from 'react';

interface Props {
  onButtonClick: (value: string) => void;
}

function ButtonHandler({ onButtonClick }: Props) {
  // 使用 useCallback 缓存 handleClick 函数引用
  const handleClick = useCallback(
    (event: React.MouseEvent<HTMLButtonElement>) => {
      onButtonClick('Clicked');
    },
    [onButtonClick],
  );

  // 当 onButtonClick 变化时,handleClick 函数引用才会重新创建
  console.log('Handle Click:', handleClick);

  return (
    <button onClick={handleClick}>
      Click me
    </button>
  );
}

在这个例子中,我们有一个 ButtonHandler 组件,它接收一个回调函数 onButtonClick 作为属性。我们使用 useCallback 来缓存 handleClick 函数引用,该函数在按钮被点击时调用 onButtonClick

这样做的好处是,如果 onButtonClick 在多次渲染中保持不变,我们就无需每次都创建新的 handleClick 函数引用,从而降低了内存开销并提高了性能。同时,由于 handleClick 函数引用是稳定的,React 可以更高效地进行事件处理和比较,避免了不必要的子组件重新渲染。

拓展: 在某些情况下,你可能希望在 useCallback 中依赖其他状态或变量,但又不想触发函数引用的重新创建。这时,你可以使用 useRef 来存储这些变量,并将其添加到 useCallback 的依赖项数组中。因为 useRef 返回的对象引用始终不变,所以即使其 .current 属性发生变化,也不会触发 useCallback 中函数引用的重新创建。

以下是一个使用 useRef 拓展 useCallback 的 TypeScript 示例:

typescript 复制代码
import { useCallback, useRef } from 'react';

interface Props {
  onButtonClick: (value: string) => void;
}

function ButtonHandler({ onButtonClick }: Props) {
  const counterRef = useRef(0);
  
  // 使用 useCallback 缓存 handleClick 函数引用
  const handleClick = useCallback(
    (event: React.MouseEvent<HTMLButtonElement>) => {
      onButtonClick(`Clicked ${counterRef.current}`);
    },
    [onButtonClick], // 注意这里没有包含 counterRef
  );

  // 更新 counterRef 的 .current 属性,但不会触发 handleClick 函数引用的重新创建
  useEffect(() => {
    counterRef.current++;
  }, []);

  return (
    <button onClick={handleClick}>
      Click me
    </button>
  );
}

在这个扩展的例子中,我们在 useCallback 中依赖了一个通过 useRef 创建的计数器 counterRef。尽管 counterRef.current 的值在每次渲染后都会增加,但由于我们只在 useEffect 中更新它,而没有将其添加到 useCallback 的依赖项数组中,因此 handleClick 函数引用不会因 counterRef 的变化而重新创建。这使得我们可以在保持函数引用稳定的同时,灵活地在函数内部访问和使用可变的状态或变量。


好了,今天的内容就到分享这里啦,很享受与大家一起学习,沟通交流问题,如果喜欢的话,请为我点个赞吧 !👍

作者:chenuvi

邮箱: chenui@outlook.com

plus: 最近在看工作机会,base 上海,有合适的前端岗位希望可以推荐一下啦!

相关推荐
fishmemory7sec7 分钟前
Electron 主进程与渲染进程、预加载preload.js
前端·javascript·electron
fishmemory7sec10 分钟前
Electron 使⽤ electron-builder 打包应用
前端·javascript·electron
豆豆1 小时前
为什么用PageAdmin CMS建设网站?
服务器·开发语言·前端·php·软件构建
twins35202 小时前
解决Vue应用中遇到路由刷新后出现 404 错误
前端·javascript·vue.js
qiyi.sky2 小时前
JavaWeb——Vue组件库Element(3/6):常见组件:Dialog对话框、Form表单(介绍、使用、实际效果)
前端·javascript·vue.js
煸橙干儿~~2 小时前
分析JS Crash(进程崩溃)
java·前端·javascript
安冬的码畜日常2 小时前
【D3.js in Action 3 精译_027】3.4 让 D3 数据适应屏幕(下)—— D3 分段比例尺的用法
前端·javascript·信息可视化·数据可视化·d3.js·d3比例尺·分段比例尺
l1x1n03 小时前
No.3 笔记 | Web安全基础:Web1.0 - 3.0 发展史
前端·http·html
昨天;明天。今天。3 小时前
案例-任务清单
前端·javascript·css
zqx_74 小时前
随记 前端框架React的初步认识
前端·react.js·前端框架