useMemo vs useCallback:React 性能优化的两大利器

在这里插入图片描述

文章目录

    • 什么是 useMemo?
      • 基本语法
      • 使用场景
      • 实际例子
    • 什么是 useCallback?
      • 基本语法
      • 使用场景
      • 实际例子
    • 核心区别对比
    • 什么时候使用它们?
      • 使用 useMemo 的时机
      • 使用 useCallback 的时机
    • 常见误区和注意事项
      • 误区 1:过度使用
      • 误区 2:依赖数组不正确
      • 误区 3:在条件语句中使用
    • 实际应用示例


在 React 开发中,性能优化始终是一个重要话题。随着应用复杂度的增加,不必要的重新渲染和重复计算会严重影响用户体验。React 提供了两个强大的 Hook —— useMemouseCallback 来帮助解决这些问题。虽然它们都用于性能优化,但各有不同的使用场景和目的。

什么是 useMemo?

useMemo 是一个用于缓存计算结果的 Hook。它会在依赖项发生变化时才重新计算值,否则返回上次缓存的结果。

基本语法

const memoizedValue = useMemo(() => {// 执行昂贵的计算return expensiveCalculation(a, b);
}, [a, b]); // 依赖数组

使用场景

  1. 昂贵的计算操作:避免在每次渲染时重复执行复杂计算
  2. 避免创建新的对象或数组:防止子组件不必要的重新渲染

实际例子

import React, { useMemo, useState } from 'react';function ExpensiveComponent({ items, filter }) {const [count, setCount] = useState(0);// 没有使用 useMemo - 每次渲染都会重新计算const expensiveValue = items.filter(item => item.category === filter).reduce((sum, item) => sum + item.price, 0);// 使用 useMemo - 只有当 items 或 filter 改变时才重新计算const memoizedValue = useMemo(() => {console.log('重新计算昂贵的值');return items.filter(item => item.category === filter).reduce((sum, item) => sum + item.price, 0);}, [items, filter]);return (<div><p>计算结果: {memoizedValue}</p><p>计数: {count}</p><button onClick={() => setCount(count + 1)}>点击计数 (不会触发重新计算)</button></div>);
}

什么是 useCallback?

useCallback 是一个用于缓存函数的 Hook。它返回一个记忆化的回调函数,只有在依赖项发生变化时才会返回新的函数。

基本语法

const memoizedCallback = useCallback(() => {// 函数逻辑doSomething(a, b);
}, [a, b]); // 依赖数组

使用场景

  1. 传递给子组件的回调函数:避免子组件因为新函数引用而重新渲染
  2. 作为其他 Hook 的依赖:确保依赖数组的稳定性

实际例子

import React, { useCallback, useState, memo } from 'react';// 子组件使用 memo 包装,只有 props 改变时才重新渲染
const ChildComponent = memo(({ onClick, name }) => {console.log(`${name} 组件渲染了`);return <button onClick={onClick}>点击 {name}</button>;
});function ParentComponent() {const [count, setCount] = useState(0);const [name, setName] = useState('');// 没有使用 useCallback - 每次渲染都会创建新函数const handleClick1 = () => {console.log('点击了按钮1');};// 使用 useCallback - 函数引用保持稳定const handleClick2 = useCallback(() => {console.log('点击了按钮2');}, []); // 空依赖数组,函数永远不会改变// 依赖于 count 的回调函数const handleClick3 = useCallback(() => {console.log(`当前计数: ${count}`);}, [count]); // 只有 count 改变时才创建新函数return (<div><p>计数: {count}</p><input value={name} onChange={(e) => setName(e.target.value)}placeholder="输入名称"/><button onClick={() => setCount(count + 1)}>增加计数</button>{/* 每次父组件重新渲染时,子组件也会重新渲染 */}<ChildComponent onClick={handleClick1} name="按钮1(未优化)" />{/* 只有当 handleClick2 改变时,子组件才会重新渲染 */}<ChildComponent onClick={handleClick2} name="按钮2(已优化)" />{/* 只有当 count 改变时,子组件才会重新渲染 */}<ChildComponent onClick={handleClick3} name="按钮3(依赖count)" /></div>);
}

核心区别对比

特性useMemouseCallback
缓存对象缓存计算结果(值)缓存函数本身
返回值返回计算后的值返回记忆化的函数
主要用途避免重复计算避免函数重新创建
典型场景复杂计算、数据处理事件处理器、回调函数
性能影响减少计算开销减少子组件重新渲染

什么时候使用它们?

使用 useMemo 的时机

  1. 计算开销很大:复杂的数学运算、大数据处理
  2. 创建复杂对象:避免每次渲染都创建新的对象或数组
  3. 作为其他 Hook 的依赖:确保依赖的稳定性
// ✅ 适合使用 useMemo
const expensiveCalculation = useMemo(() => {return heavyProcessing(largeDataSet);
}, [largeDataSet]);// ✅ 避免创建新对象
const userInfo = useMemo(() => ({name: user.name,email: user.email,isActive: user.status === 'active'
}), [user.name, user.email, user.status]);

使用 useCallback 的时机

  1. 传递给子组件的函数:特别是使用了 React.memo 的子组件
  2. 作为 useEffect 的依赖:避免 effect 不必要的重新执行
  3. 自定义 Hook 中的函数:保持 API 的稳定性
// ✅ 适合使用 useCallback
const handleSubmit = useCallback((formData) => {submitForm(formData);
}, []);// ✅ 作为 useEffect 的依赖
const fetchData = useCallback(async () => {const data = await api.getData(id);setData(data);
}, [id]);useEffect(() => {fetchData();
}, [fetchData]);

常见误区和注意事项

误区 1:过度使用

不是所有的计算都需要使用 useMemo,也不是所有的函数都需要使用 useCallback。这些 Hook 本身也有开销,只有在确实需要优化时才使用。

// ❌ 不需要优化的简单计算
const simpleValue = useMemo(() => a + b, [a, b]);// ✅ 直接计算即可
const simpleValue = a + b;

误区 2:依赖数组不正确

确保依赖数组包含所有使用到的变量,否则可能导致 bug。

// ❌ 缺少依赖
const memoizedValue = useMemo(() => {return calculate(a, b, c);
}, [a, b]); // 缺少 c// ✅ 完整的依赖
const memoizedValue = useMemo(() => {return calculate(a, b, c);
}, [a, b, c]);

误区 3:在条件语句中使用

Hook 必须在函数组件的顶层调用,不能在条件语句中使用。

// ❌ 错误的使用方式
if (condition) {const value = useMemo(() => calculate(), []);
}// ✅ 正确的使用方式
const value = useMemo(() => {if (condition) {return calculate();}return defaultValue;
}, [condition]);

实际应用示例

让我们看一个综合使用 useMemouseCallback 的实际例子:

import React, { useState, useMemo, useCallback, memo } from 'react';// 模拟昂贵的计算函数
const expensiveCalculation = (items) => {console.log('执行昂贵计算...');return items.reduce((sum, item) => sum + item.value * item.quantity, 0);
};// 子组件
const ProductItem = memo(({ product, onUpdate, onDelete }) => {console.log(`渲染产品: ${product.name}`);return (<div><span>{product.name}: {product.value} x {product.quantity}</span><button onClick={() => onUpdate(product.id)}>更新</button><button onClick={() => onDelete(product.id)}>删除</button></div>);
});function ShoppingCart() {const [products, setProducts] = useState([{ id: 1, name: '商品A', value: 100, quantity: 2 },{ id: 2, name: '商品B', value: 200, quantity: 1 },{ id: 3, name: '商品C', value: 50, quantity: 3 }]);const [discount, setDiscount] = useState(0);// 使用 useMemo 缓存总价计算const totalPrice = useMemo(() => {const basePrice = expensiveCalculation(products);return basePrice * (1 - discount / 100);}, [products, discount]);// 使用 useCallback 缓存事件处理函数const handleUpdateProduct = useCallback((productId) => {setProducts(prev => prev.map(p => p.id === productId ? { ...p, quantity: p.quantity + 1 }: p));}, []);const handleDeleteProduct = useCallback((productId) => {setProducts(prev => prev.filter(p => p.id !== productId));}, []);return (<div><h2>购物车</h2>{products.map(product => (<ProductItemkey={product.id}product={product}onUpdate={handleUpdateProduct}onDelete={handleDeleteProduct}/>))}<div><label>折扣(%): <input type="number" value={discount}onChange={(e) => setDiscount(Number(e.target.value))}/></label></div><h3>总价: ¥{totalPrice.toFixed(2)}</h3></div>);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/web/83816.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++ 记录

1. 字符串查找字符 bool findMap(char ch){string mapper "aeiouAEIOU";return mapper.find(ch) ! string::npos;} 2.substr函数 string substr(size_t pos 0, size_t len npos) const; 3.to_string && stoi 函数 iota 填充一个范围&#xff0…

朴朴超市小程序 sign-v2 分析

声明 本文章中所有内容仅供学习交流使用&#xff0c;不用于其他任何目的&#xff0c;抓包内容、敏感网址、数据接口等均已做脱敏处理&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff01; 部分python代码 cp execjs.compile(…

Azure 机器学习初学者指南

Azure 机器学习初学者指南 在我们的初学者指南中探索Azure机器学习&#xff0c;了解如何设置、部署模型以及在Azure生态系统中使用AutoML & ML Studio。Azure 机器学习 &#xff08;Azure ML&#xff09; 是一项全面的云服务&#xff0c;专为机器学习项目生命周期而设计&am…

HTML 从入门到起飞 · 系列合集:一站式学习不掉线

一、&#x1f4bb;计算机基础 &#x1f31f;艾伦麦席森图灵&#x1f31f; ⚔️ 二战时期&#xff0c;破译了德军的战争编码——英格玛。 &#x1f54a;️ 让二战提前2年结束&#xff0c;拯救了上千万人的生命。 &#x1f3c6; 设立图灵奖&#xff0c;被后人誉为&#xff1a;&qu…

NodeJS的yarn和npm作用和区别,为什么建议用yarn

一、yarn和npm作用和区别 yarn异步执行安卓&#xff0c;npm同步执行安装 yarn会复用&#xff0c;已经安装的不会再次安装。不过新版npm已经解决了。 Yarn安装信息干净一点&#xff0c;npm会罗列包信息 下面是关于 Node.js 中 npm 和 yarn 的完整对比与说明&#xff0c;帮你…

青少年编程与数学 01-011 系统软件简介 19 SSMS 数据库管理工具

青少年编程与数学 01-011 系统软件简介 19 SSMS 数据库管理工具 一、历史发展二、技术特色三、主要功能四、应用场景五、产品版本六、使用方法七、市场地位八、发展前景全文总结 **摘要&#xff1a;**SSMS 是微软开发的数据库管理工具&#xff0c;首次引入于 SQL Server 2005&a…

React配置别名路径完整指南

文章目录 什么是别名路径配置方法1. Create React App 项目方法一&#xff1a;使用jsconfig.json&#xff08;推荐&#xff09;方法二&#xff1a;使用CRACO&#xff08;React App Rewired的替代方案&#xff09; 2. Vite 项目修改vite.config.js配置TypeScript支持 3. Next.js…

本周大模型新动向:自主机器学习、状态机推理、上下文压缩

点击蓝字 关注我们 AI TIME欢迎每一位AI爱好者的加入&#xff01; 01 Sentinel: Attention Probing of Proxy Models for LLM Context Compression with an Understanding Perspective 检索增强型生成&#xff08;RAG&#xff09;通过外部上下文增强了大型语言模型&#xff08;…

从零开始掌握 Docker:核心命令与实践指南

从零开始掌握 Docker&#xff1a;核心命令与实践指南 摘要&#xff1a; 本文系统整理了 Docker 的核心概念与常用命令&#xff0c;涵盖镜像管理、容器操作、数据存储、网络配置、Compose 编排以及 Dockerfile 构建等内容。通过清晰的命令示例和实用说明&#xff0c;帮助开发者…

RDMA简介7之RoCE v2可靠传输

可靠传输技术旨在通过多种方法确保数据包在传输过程中不会丢失或损坏&#xff0c;同时保证数据包按发送顺序到达接收端&#xff0c;其要求在链路发生丢包或网络发生拥塞等情况下能够完全保证数据包的正确性同时尽可能地提高传输速率。RoCE v2协议实现可靠传输的技术手段共有三种…

java33

1.特殊文件 属性文件properties 配置文件xml 注意&#xff1a;XML文件可以直接在浏览器里面打开&#xff1a; 了解知识&#xff1a; 2.日志技术 3.单元测试 注意&#xff1a;点击方法内部再右键运行是测试改方法的&#xff0c;点击类名再右键运行才是测试整个测试类的&#xff…

OSI 七层网络模型

目录 OSI 七层网络模型七层模型1. 物理层&#xff08;Physical Layer&#xff09;2. 数据链路层&#xff08;Data Link Layer&#xff09;3. 网络层&#xff08;Network Layer&#xff09;4. 传输层&#xff08;Transport Layer&#xff09;5. 会话层&#xff08;Session Layer…

Spring Boot 4.0.0 新特性详解:深入解读 Spring Framework 7.0.0

你是否注意到创建新 Spring Boot 项目时出现的最新选项?Spring Boot 4.0.0 预览版现已发布,基于最新的 Spring Framework 7.0.0 🌱。这个版本引入了众多激动人心的新特性,不仅提升了开发效率,改善了空值安全性,还简化了 Web 应用程序的开发流程。本文将深入探讨这些重要…

从0到1构建高并发秒杀系统:实战 RocketMQ 异步削峰与Redis预减库存

&#x1f680;从0到1构建高并发秒杀系统&#xff1a;实战 RocketMQ 异步削峰与Redis预减库存 &#x1f4d6;一、 简介 在电商、抢票等高并发场景中&#xff0c;秒杀系统面临着“高并发、库存稀缺、易超卖、系统易崩”的严峻挑战。传统的同步处理架构难以支撑海量请求并发下的性…

OpenCV CUDA模块图像变形------对图像进行任意形式的重映射(Remapping)操作函数remap()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 该函数根据给定的映射表 xmap 和 ymap 对图像进行 任意形式的重映射&#xff08;Remapping&#xff09;操作&#xff0c;是 GPU 加速版本的图像几…

PC 端常用 UI 组件库

一、前言 随着企业级应用、后台管理系统、数据平台等项目的不断发展&#xff0c;前端开发已经不再局限于移动端和响应式布局&#xff0c;而是越来越多地聚焦于 PC 端系统的构建。为了提升开发效率、统一设计风格并保障用户体验&#xff0c;使用成熟的 UI 组件库 成为了现代前端…

pikachu靶场通关笔记31 文件包含02之远程文件包含

目录 一、文件包含功能 二、文件包含Vulnerability 二、远程文件包含 三、环境配置 1、进入靶场 2、搭建环境 &#xff08;1&#xff09;定位php.ini文件 &#xff08;2&#xff09;修改php.ini文件 四、源码分析 五、渗透实战 1、选择科比 2、执行phpinfo &…

QT集成Boost库

在Windows平台上&#xff0c;使用Qt集成Boost库&#xff0c;并基于MSVC编译器在CMake文件中加载&#xff0c;可以按照以下步骤进行配置。 Boost库的编译 如果Boost库未预编译&#xff0c;需要手动编译&#xff0c;解压zip到D:\Library\boost_1_87_0&#xff0c;打开cmd命令行…

MySQL从库复制延迟的监测

目录 ⏱️ 一、原生内置方法❤️ 二、心跳表工具&#xff08;如pt-heartbeat&#xff09;⚙️ 三、MySQL 8.0 增强方案&#x1f4ca; 四、各方案对比总结&#x1f48e; 五、选择建议 MySQL从库复制延迟的监测是保障数据一致性和读写分离可靠性的关键环节&#xff0c;以下是主流…

slam--最小二乘问题--凹凸函数

最小二乘问题 最小二乘问题标准公式 残差函数&#xff0c;线性和非线性最小二乘 最小二乘问题的两种写法&#xff1a; 目标 找到 x使得预测值 Ax与观测值 b 的残差平方和最小。 范数和范数平方 线性最小二乘 一般形式&#xff1a; 残差 rAx−b是x 的线性函数。 目标函数是…