React开发者的烦恼:To `useMemo` or Not to `useMemo`

今天来讨论一下如何避免滥用 React useMemo 这个 hooks。众所周知 React 有了函数式组件后 推出了一系列官方的 hooks,一部分是用来优化组件渲染,提升性能的。useMemo 就是其中之一,不使用 useMemo 或者过度使用 useMemo 都会带来性能问题。

什么是 useMemo

useMemo 是一个 React Hook,它在每次重新渲染的时候能够缓存计算的结果。

js 复制代码
const cachedValue = useMemo(calculateValue, dependencies)

参数

  • calculateValue:要缓存计算值的函数。它应该是一个没有任何参数的纯函数,并且可以返回任意类型。React 将会在首次渲染时调用该函数;在之后的渲染中,如果 dependencies 没有发生变化,React 将直接返回相同值。否则,将会再次调用 calculateValue 并返回最新结果,然后缓存该结果以便下次重复使用。
  • dependencies:所有在 calculateValue 函数中使用的响应式变量组成的数组。响应式变量包括 props、state 和所有你直接在组件中定义的变量和函数。如果你在代码检查工具中 配置了 React,它将会确保每一个响应式数据都被正确地定义为依赖项。依赖项数组的长度必须是固定的并且必须写成 [dep1, dep2, dep3] 这种形式。React 使用 Object.is 将每个依赖项与其之前的值进行比较。

返回值

在初次渲染时,useMemo 返回不带参数调用 calculateValue 的结果。

在接下来的渲染中,如果依赖项没有发生改变,它将返回上次缓存的值;否则将再次调用 calculateValue,并返回最新结果。

作用

  • 跳过代价昂贵的重新计算
  • 跳过组件的重新渲染
  • 记忆另一个 Hook 的依赖
  • 记忆一个函数

好了现在我们已经了解了什么是 useMemo,接下来看一下看一下具体使用情况。

什么情况下使用 useMemo

下面几种情况适用于使用 useMemo

复杂计算

在上面 useMemo 的一个作用就是跳过昂贵的重新计算。如下代码所示:

js 复制代码
function complexCalculation(list) {
  // 复杂计算
  return result;
}

function MyComponent({ list }) {
  const memoizedResult = useMemo(() => {
    return complexCalculation(list)
  }, [list]);

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

保持引用等价

上面提到作用之一是避免组件重新渲染。useMemo 有助于在将对象或数组作为道具传递时保持相同的引用,从而避免不必要的渲染。如下所示:

js 复制代码
function MyComponent({ values }) {
  const memoizedValues = useMemo(() => values, [values]);

  return <MemoizedChildComponent values={memoizedValues} />;
};

function ChildComponent({ values }) {
  // ...
});

const MemoizedChildComponent = React.memo(ChildComponent)

对大列表进行过滤或排序

当处理大型数据集时,useMemo 可以用于过滤或排序可以防止这些操作在每次渲染期间重新运行,从而节省宝贵的计算能力。如下所示:

js 复制代码
function MyComponent({ list }) {
  const filteredList = useMemo(() => {
    return list.filter(item => item.active);
  }, [list]);

  return <ListDisplay items={filteredList} />;
};

什么情况下不使用 useMemo

有时候过度使用 useMemo 反而会适得其反。

简单计算

对于基本计算,引入useMemo可能会使事情变得复杂而不是有帮助。如下所示:

js 复制代码
function MyComponent({ value }) {
  // 不推荐使用简单计算
  const multipliedValue = useMemo(() => {
    return value * 2;
  }, [value]);

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

记录原始值

记录原始值(例如数字或字符串)通常是多余的,因为它们不会导致不必要的渲染。如下所示:

js 复制代码
function MyComponent({ value }) {
  // 不推荐用于原始值
  const memoizedValue = useMemo(() => value + 1, [value]);

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

// 依赖关系频繁变化

// 如果输入变化很大,`useMemo`就会失去效率,因为它最终会经常重新计算。

function MyComponent({ frequentlyChangingValue }) {
  // 不建议频繁更改依赖项
  const memoizedValue = useMemo(() => {
    return expensiveCalculation(frequentlyChangingValue);
  }, [frequentlyChangingValue]);

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

小结

hooks 再好也不能贪多哦,要适当使用 useMemo 优化代码避免额外的计算和重复渲染,优化代码性能,过度使用反而可能会适得其反。

相关推荐
_lucas12 分钟前
给知识库网站接入AI问答
前端·ai编程·全栈
浮生望18 分钟前
React useState 深度解析:异步更新、闭包陷阱与惰性初始化的底层逻辑
react.js
前端糕手1 小时前
前端面试题大全:JavaScript + Vue3 + React + TypeScript + 工程化 + 性能优化
前端
我不叫武2 小时前
一个用 Rust 写的离线编码查询桌面工具
前端
Web4Browser3 小时前
指纹浏览器 API 自动化怎么接:启动 Profile、获取 CDP 端点并连接自动化框架
前端·网络·typescript·自动化
Patrick_Wilson3 小时前
从 React 到 Flutter:写给前端的一张跨端知识地图
前端·flutter·react.js
颜酱3 小时前
06 | 把 meta_config 同步进 MySQL(生成阶段)
前端·人工智能·后端
OpenTiny社区4 小时前
Loop Engineering:让 AI Agent 自己跑起来的工程方法
前端·github
IT_陈寒4 小时前
SpringBoot自动配置坑了我一周,原来问题这么蠢!
前端·人工智能·后端
kyriewen4 小时前
我review了一份Vibe Coding写的前端代码——能跑,但5个地方迟早要命
前端·javascript·ai编程