React useMemo(当依赖项未变化,重复渲染时直接返回上一次缓存计算结果,而非重新执行计算)

文章目录

  • [React `useMemo` 介绍:提升组件性能的关键利器](#React useMemo 介绍:提升组件性能的关键利器)
  • [3. 在 Next.js App Router 中使用 useMemo(示例)](#3. 在 Next.js App Router 中使用 useMemo(示例))
    • [下面示例基于 **Next.js 13+ App Router**,展示如何在页面组件中使用 `useMemo` 来处理 expensive computation。](#下面示例基于 Next.js 13+ App Router,展示如何在页面组件中使用 useMemo 来处理 expensive computation。)
      • [📁 项目结构](#📁 项目结构)
      • [📄 `app/page.tsx`](#📄 app/page.tsx)
      • [🔍 代码解析](#🔍 代码解析)
    • 下面是去掉useMemo的版本,我们来看看是什么情况
  • [4. 使用 useMemo 的注意事项](#4. 使用 useMemo 的注意事项)
  • [5. 总结](#5. 总结)

React useMemo 介绍:提升组件性能的关键利器

在 React 开发中,组件渲染性能是一个永恒的话题。当组件包含复杂计算、依赖重的派生数据,或在高频渲染场景中运行时,就会带来性能下降。这时,React 提供的 useMemo 就能发挥重要作用------用于缓存计算结果,避免不必要的重复运算,从而提升应用性能。

1. useMemo 是什么?

useMemo 是 React 的一个 Hook,用于缓存某个计算过程的结果(memoized value)。其核心思想是:

当依赖项未变化时,重复渲染时直接返回上一次的计算结果,而不是重新执行计算。

基本语法

ts 复制代码
const memoizedValue = useMemo(() => {
  return expensiveComputation(a, b);
}, [a, b]);
  • 第一个参数是返回计算结果的函数。
  • 第二个参数是依赖项数组,当依赖项变化时会重新执行计算。

2. 为什么需要 useMemo

场景一:昂贵计算

例如需要处理大量数据、复杂筛选或排序逻辑,如果每次渲染都触发,会导致卡顿。

场景二:避免子组件不必要渲染

当 memoized 值作为 props 传递给子组件时,如果不使用 useMemo,每次渲染都会生成新引用,从而导致子组件重新渲染。

场景三:稳定引用(如避免 useEffect 重复触发)


3. 在 Next.js App Router 中使用 useMemo(示例)

下面示例基于 Next.js 13+ App Router ,展示如何在页面组件中使用 useMemo 来处理 expensive computation。

📁 项目结构

复制代码
app/
  page.tsx

📄 app/page.tsx

ts 复制代码
"use client";

import { useMemo, useState } from "react";

export default function HomePage() {
  const [count, setCount] = useState(0);
  const [query, setQuery] = useState("");

  // 模拟昂贵计算(比如过滤大型数据列表)
  const filteredList = useMemo(() => {
    console.log("执行 expensive computation...");
    const data = Array.from({ length: 50000 }, (_, i) => `Item ${i}`);
    return data.filter((item) => item.includes(query));
  }, [query]);

  return (
    <main style={{ padding: 20 }}>
      <h1>React useMemo 示例(Next.js App Router)</h1>

      <div style={{ marginTop: 20 }}>
        <input
          value={query}
          onChange={(e) => setQuery(e.target.value)}
          placeholder="输入查询关键字(触发 expensive computation)"
          style={{ padding: 8, width: 300 }}
        />
      </div>

      <div style={{ marginTop: 20 }}>
        <button
          onClick={() => setCount(count + 1)}
          style={{ padding: "6px 12px" }}
        >
          点击计数:{count}
        </button>
      </div>

      <div style={{ marginTop: 20 }}>
        <h3>过滤结果({filteredList.length} items)</h3>
        <ul>
          {filteredList.slice(0, 20).map((item) => (
            <li key={item}>{item}</li>
          ))}
        </ul>
        <small>(仅显示前 20 条)</small>
      </div>
    </main>
  );
}

在输入框输入会触发console.log("执行 expensive computation..."); 打印,点击计数不会,这样就避免了不必要的大规模计算

🔍 代码解析

  • filteredList 依赖 query,当 query 改变时才会重新执行过滤。
  • 点击"计数"按钮只修改 count,不触发 expensive computation。
  • 控制台可以观察到 expensive computation... 打印次数明显减少。

下面是去掉useMemo的版本,我们来看看是什么情况

ts 复制代码
// app/page.tsx (Next.js 13 app-router, client component)

"use client";

import { useState } from "react"; // 移除了 useMemo 导入

export default function HomePage() {
  const [count, setCount] = useState(0);
  const [query, setQuery] = useState("");

  // 直接计算而不使用 useMemo(每次渲染都会重新执行)
  console.log("执行 expensive computation..."); // 注意:这条日志会在每次渲染时触发
  const data = Array.from({ length: 50000 }, (_, i) => `Item ${i}`);
  const filteredList = data.filter((item) => item.includes(query));

  return (
    <main style={{ padding: 20 }}>
      <h1>React useMemo 示例(Next.js App Router)</h1>

      <div style={{ marginTop: 20 }}>
        <input
          value={query}
          onChange={(e) => setQuery(e.target.value)}
          placeholder="输入查询关键字(触发 expensive computation)"
          style={{ padding: 8, width: 300 }}
        />
      </div>

      <div style={{ marginTop: 20 }}>
        <button
          onClick={() => setCount(count + 1)}
          style={{ padding: "6px 12px" }}
        >
          点击计数:{count}
        </button>
      </div>

      <div style={{ marginTop: 20 }}>
        <h3>过滤结果({filteredList.length} items)</h3>
        <ul>
          {filteredList.slice(0, 20).map((item) => (
            <li key={item}>{item}</li>
          ))}
        </ul>
        <small>(仅显示前 20 条)</small>
      </div>
    </main>
  );
}

发现点击计数也会触发console.log("执行 expensive computation..."); 打印,相当于下面代码也被执行,非常耗资源:

ts 复制代码
const data = Array.from({ length: 50000 }, (_, i) => `Item ${i}`);
const filteredList = data.filter((item) => item.includes(query));

4. 使用 useMemo 的注意事项

❗不要过度使用 useMemo

过度缓存会带来额外内存和计算开销,如果计算量很小,反而得不偿失。

❗依赖项一定要正确填写

依赖项写错会导致:

  • 缓存不更新(bug)
  • 每次都重新执行(性能浪费)

✔ 最佳实践

  • 用于昂贵计算(排序、过滤、格式化大型数据)
  • 用于稳定引用(如传给 memoized 子组件)
  • 用于避免 useEffect 不必要触发

5. 总结

useMemo 是 React 提供的重要性能优化工具,适用于缓存昂贵计算或保持引用稳定。在 Next.js App Router 中,它和普通的 React 组件用法完全一致,但要注意不要滥用。

在真实项目中,合理应用 useMemo 可以显著提高渲染性能,让页面更加流畅。

相关推荐
顾安r25 分钟前
1.1 脚本网页 战推棋
java·前端·游戏·html·virtualenv
一颗小青松26 分钟前
vue 腾讯地图经纬度转高德地图经纬度
前端·javascript·vue.js
Justin3go8 小时前
HUNT0 上线了——尽早发布,尽早发现
前端·后端·程序员
怕浪猫9 小时前
第一章 JSX 增强特性与函数组件入门
前端·javascript·react.js
铅笔侠_小龙虾9 小时前
Emmet 常用用法指南
前端·vue
钦拆大仁9 小时前
跨站脚本攻击XSS
前端·xss
VX:Fegn089510 小时前
计算机毕业设计|基于springboot + vue校园社团管理系统(源码+数据库+文档)
前端·数据库·vue.js·spring boot·后端·课程设计
ChangYan.11 小时前
直接下载源码但是执行npm run compile后报错
前端·npm·node.js
skywalk816311 小时前
在 FreeBSD 上可以使用的虚拟主机(Web‑Hosting)面板
前端·主机·webmin
ohyeah12 小时前
深入理解 React 中的 useRef:不只是获取 DOM 元素
前端·react.js