当前位置: 首页 > news >正文

react useMemo Hook详解

什么是 useMemo Hook?

useMemo 是一个 React Hook,用于缓存计算结果,避免在每次组件渲染时重复执行昂贵的计算。它通过记忆计算的值,只有在依赖项变化时才会重新计算,从而优化性能。

简单来说,useMemo 让你的计算结果“记住”上一次的值,减少不必要的计算开销。


为什么需要 useMemo

在 React 函数组件中,每次渲染都会重新执行组件函数体。如果有复杂或耗时的计算(比如过滤大数组、复杂数学运算),每次渲染都重复计算会影响性能。

useMemo 解决了这个问题,通过缓存计算结果,只有当依赖的变量变化时才重新计算。

适用场景

  • 复杂的计算逻辑(比如处理大数据、排序、过滤)
  • 需要保持引用稳定的对象或数组(避免子组件不必要渲染)
  • 优化性能,减少重复计算

useMemo 的基本语法

useMemo 接收两个参数:

  1. 计算函数:返回你想缓存的值(可以是任何类型:数字、对象、数组等)。
  2. 依赖数组:控制何时重新计算,只有依赖项变化时才重新运行计算函数。

基本结构:

import { useMemo } from 'react';function MyComponent() {const memoizedValue = useMemo(() => {// 耗时计算逻辑return expensiveCalculation();}, [/* 依赖项 */]);return <div>{memoizedValue}</div>;
}
  • 返回值useMemo 返回缓存的计算结果(值),在依赖项不变的情况下,始终返回相同的引用。

useMemo 的使用场景

以下通过具体例子,带你理解 useMemo 的实际用途。

1. 优化昂贵计算

假设你有一个需要过滤和排序大数组的计算,useMemo 可以避免每次渲染都重复执行。

示例代码

import { useState, useMemo } from 'react';function FilterList() {const [numbers, setNumbers] = useState([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);const [filter, setFilter] = useState(5);// 使用 useMemo 缓存过滤结果const filteredNumbers = useMemo(() => {console.log('执行过滤计算');return numbers.filter(num => num > filter).sort((a, b) => b - a);}, [numbers, filter]); // 依赖 numbers 和 filterreturn (<div><inputtype="number"value={filter}onChange={e => setFilter(Number(e.target.value))}/><ul>{filteredNumbers.map(num => (<li key={num}>{num}</li>))}</ul></div>);
}

运行效果

  • filteredNumbers 是缓存的过滤和排序结果。
  • 只有当 numbersfilter 变化时,useMemo 才会重新计算(控制台打印“执行过滤计算”)。
  • 如果没有 useMemo,每次组件渲染(比如无关的状态变化)都会重新过滤和排序,浪费性能。

2. 保持对象或数组引用稳定(搭配 React.memo

如果一个对象或数组作为 props 传递给子组件,而子组件使用了 React.memo,每次渲染生成的新对象/数组会导致子组件重新渲染。useMemo 可以缓存对象/数组,保持引用稳定。

示例代码

import { useState, useMemo } from 'react';
import { memo } from 'react';// 子组件使用 React.memo
const ChildComponent = memo(({ config }) => {console.log('ChildComponent 渲染了');return <div>配置: {config.name}</div>;
});function ParentComponent() {const [count, setCount] = useState(0);// 使用 useMemo 缓存对象const config = useMemo(() => {return { name: '我的配置' };}, []); // 空数组,config 永不重新生成return (<div><p>计数: {count}</p><button onClick={() => setCount(count + 1)}>加1</button><ChildComponent config={config} /></div>);
}

运行效果

  • 点击“加1”按钮,count 变化,ParentComponent 重新渲染。
  • ChildComponent 不会重新渲染,因为 config 的引用(由 useMemo 缓存)没变。
  • 如果不用 useMemo,每次渲染都会生成新对象 { name: '我的配置' },导致 ChildComponent 不必要地重新渲染。

useMemo vs. 其他 Hooks

  • useCallback 的区别

    • useMemo 缓存计算结果(可以是任何值:对象、数组、数字等)。
    • useCallback 缓存函数,返回函数引用。
    • useCallback(fn, deps) 等价于 useMemo(() => fn, deps)
    // useMemo 缓存对象
    const memoizedValue = useMemo(() => ({ name: '小明' }), []);// useCallback 缓存函数
    const memoizedFn = useCallback(() => console.log('点击'), []);
    
  • useEffect 的关系

    • useMemo 用于缓存计算结果,避免重复计算。
    • useEffect 用于处理副作用(比如 API 请求、DOM 操作)。
    • 有时 useMemouseCallback 一起用于优化 useEffect 的依赖。

注意事项(新手常见坑)

  1. 不要滥用 useMemo

    • useMemo 本身有内存开销(缓存结果),只在计算昂贵或需要保持引用稳定时使用。
    • 如果计算很简单(比如 a + b),直接计算比用 useMemo 更高效。
  2. 依赖数组要正确填写

    • 确保依赖数组包含计算函数中用到的所有变量,否则可能导致 bug。
    • ESLint 的 react-hooks/exhaustive-deps 规则会帮你检查。
    const result = useMemo(() => {return expensiveCalculation(count); // count 必须在依赖数组中
    }, [count]);
    
  3. 引用稳定性不总是必要的

    • 如果子组件没用 React.memo,或计算结果不影响性能,可能不需要 useMemo
  4. 不要用 useMemo 替代所有状态

    • useMemo 是为优化计算设计的,不是状态管理工具。状态变化仍需用 useStateuseReducer

总结

  • useMemo 的作用:缓存计算结果,避免重复执行昂贵计算,保持引用稳定。
  • 使用场景
    • 优化复杂计算(比如过滤、排序大数据)。
    • 缓存对象/数组,配合 React.memo 避免子组件不必要渲染。
  • 语法useMemo(fn, deps),返回缓存的值,依赖项变化时重新计算。
  • 注意事项:只在需要时使用,正确声明依赖数组。
http://www.hskmm.com/?act=detail&tid=20938

相关文章:

  • react useContext 详解
  • Python技能大赛-备赛建议
  • 【软件系统架构】系列七:系统性能——操作系统性能深入解析 - 实践
  • 你的下一款定位神器,何必是GPS?Nordic带你解锁Wi-Fi SSID的隐藏潜能
  • CF407E k-d-sequence 题目分析(0929模拟赛最后一题)
  • Linux 生成随机端口
  • MATLAB 中 dsp.FFT 系统对象:从原理到实践的完整指南
  • 并发编程可见性
  • C# Devexpress GridControl实现全选功能(转载,记录)
  • github Connection reset by 20.205.243.160 port 443 fatal: Could not read from remote repository.
  • VsCode Ai插件
  • 完整教程:基于完全分布式模式部署Hadoop(喂饭教程)
  • Vue 3.6 引入 Vapor Mode,虚拟DOM已死?
  • part 10
  • Nordic发布用于nRF54L系列的nRF Connect SDK裸机选项
  • 微软SSO集成中的顺序用户ID身份验证绕过漏洞剖析
  • content和text方法的区别
  • shell脚本动态域名解析阿里云
  • 聪明的wyk
  • Windows下进程和账户权限
  • Spring Gateway动态路由实现方案 - 详解
  • Nordic 高性能无线SoC nRF54LM20A,专为低功耗蓝牙与Matter设计
  • 调用setState 之后发生了什么?
  • element plus 配置主题色
  • Python教程:解决pip安装包时报错:error: externally-managed-environment This environment is externally managed
  • 哲学家进餐问题
  • 16.1 总体主成分分析
  • 黄金分割比
  • 借助Aspose.Email,使用 Python 读取 Outlook MSG 文件
  • 痞子衡嵌入式:恩智浦i.MX RT1xxx系列MCU启动那些事(11.B)- FlexSPI NOR连接方式大全(RT1180)