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

React(四)memo、useCallback、useMemo Hook

目录

(一)memo API

1.先想一个情景

2.用法

(1)props传入普通数据类型的情况 

(2)props传入对象的情况

(3)props传入函数的情况

(4)使用自定义比较函数

3.什么时候使用memo?

(二)useMemo Hook

1.用法

2.useMemo实现组件记忆化 

3.useMemo实现函数记忆化

(三)useCallback Hook

1.用法

(四)总结


(一)memo API

memo – React 中文文档

1.先想一个情景

function App() {const [count, setCount] = useState(0)const [name, setName] = useState('csq')return (<><div>{count}</div><button onClick={()=>{setCount(count+1)}}>加1</button><Name name={name}></Name></>)
}function Name(props){console.log('Name组件重新渲染了');return (<div>{props.name}</div>)
}

点击按钮count改变后,整个App组件会重新渲染,即使Name组件的props没有改变,Name组件也被重新渲染了

如果一个过于复杂的组件的props没有改变,那么重新渲染它会增加渲染负担;

通常情况下,只要该组件的 props 没有改变,这个记忆化版本就不会在其父组件重新渲染时重新渲染。这就称之为记忆化,是一种性能优化的办法

因此,memo api的作用就体现出来了:

memo 允许你的组件在 props 没有改变的情况下跳过重新渲染。 

2.用法

const xxx = memo(function xxx(props){...}, arePropsEqual?

将需要进行记忆化的组件用memo包裹起来,通过arePropsEqual函数判断props是否变化(可自定义,不写就默认使用Object.is()判断),然后返回新的react组件

(1)props传入普通数据类型的情况 

import { memo } from 'react'
// 使用memo
function App() {const [count, setCount] = useState(0)const [name, setName] = useState('csq')return (<><div>{count}</div><button onClick={()=>{setCount(count+1)}}>加1</button><Name name={name}></Name></>)
}// 不传入arePropsEqual函数就默认用Object.is判断
const Name = memo(function Name(props){console.log('Name组件重新渲染了');return (<div>{props.name}</div>)
})

这样一来,Name组件在父组件重新渲染的时候就不会跟着渲染啦

(2)props传入对象的情况

上面提到了,不自定义比较函数的话,memo采用Object.is()方法来比较props是否变化

但是!Object.is()只能比较浅层数据是否变化,如果对复杂数据类型进行比较会发现:

结果为false!这表明了传入复杂数据类型的prop仍会导致memo组件重新渲染 

import { memo } from 'react'
// 使用memo
function App() {const [count, setCount] = useState(0)return (<><div>{count}</div><button onClick={()=>{setCount(count+1)}}>加1</button><Name data={{name:'csq'}}></Name></>)
}
// 不传入arePropsEqual函数就默认用Object.is判断
const Name = memo(function Name(props){console.log('Name组件重新渲染了',props);return (<div>{props.data.name}</div>)
})

重新渲染了又 

如何避免? 

为了最大化使用 memo 的效果,应该尽量减少 props 的变化次数 

  1. 最小化props:将对象拆分开再传
  2. 使用useMemo Hook(后面会讲到)
  3. 指定自定义比较函数(后面会讲到)

(3)props传入函数的情况

函数也是复杂数据类型中的一种,所以传入的就算是同一个函数,父组件的重新渲染也会引起记忆化的组件重新渲染,这样memo就失效了!

针对props传入的函数,就要采用另一个Hook:useCallback 来实现组件的记忆化,后面第三节就会讲到了

(4)使用自定义比较函数

一定要确保使用比较函数的时间比重新渲染要快,不然写个比较函数还浪费那么久时间简直白白干了

const Chart = memo(function Chart({ dataPoints }) {// ...
}, arePropsEqual);function arePropsEqual(oldProps, newProps) {return (oldProps.dataPoints.length === newProps.dataPoints.length &&oldProps.dataPoints.every((oldPoint, index) => {const newPoint = newProps.dataPoints[index];return oldPoint.x === newPoint.x && oldPoint.y === newPoint.y;}));
}

注意:比较函数内一定要将props的所有prop都比较到,包括函数。

避免在 arePropsEqual 中进行深比较,除非你 100% 确定你正在处理的数据结构具有已知有限的深度。深比较可能会变得非常缓慢,并且如果有人稍后更改数据结构,这可能会卡住你的应用数秒钟。

简而言之就是少用,有那个功夫用用hook肯定更快啦 

3.什么时候使用memo?

memo api并不是是个组件就给安上的,首先要考虑到组件的重新渲染对页面会不会造成影响,比如有动画的组件:重新渲染会导致动画重新播放,如果不想变化的话就加个memo

如果组件的重新渲染没有什么延迟,当然也不需要用memo 


(二)useMemo Hook

useMemo – React 中文文档

useMemo 能在每次重新渲染的时候能够缓存计算的结果。

1.用法

useMemo(calculateValue, dependencies) 

  • calculateValue:要缓存计算值的函数。React 将会在首次渲染时调用该函数;在之后的渲染中,如果 dependencies 没有发生变化,React 将直接返回相同值。否则,将会再次调用 calculateValue 并返回最新结果,然后缓存该结果以便下次重复使用。

  • dependencies:所有在 calculateValue 函数中使用的响应式变量组成的数组。React 使用 Object.is 将每个依赖项与其之前的值进行比较。 

举例:用counterFilter模拟运行时间长的函数,每次点击todo长度加1按钮都会等待0.5s

不使用useMemo的话,即使len不发生改变,count发生改变时,也会重新渲染运行counterFilter

使用useMemo()后,当len发生改变时,才会重新计算todoSlice的值,而点击count+1按钮不会造成卡顿

import { useState, useMemo } from 'react'
function App() {const [count,setCount] = useState(1)const [len, setLen] = useState(2)// 这里todos是个数组,也过不了Object.is(),所以套一层useMemoconst todos = useMemo(()=>([1, 2, 3, 4, 5, 6, 7, 8, 9]),[])const todoSlice = useMemo(() => counterFilter(todos, len),[todos, len])return (<><button onClick={() => { setLen(len + 1) }}>todo长度加1</button><button onClick={() => { setCount(count + 1) }}>count加1</button>{todoSlice.map(value => (<p key={value}>todo: {value}</p>))}<Counter count={count}></Counter></>)
}const Counter = function Name(props) {return (<div>{props.count}</div>)
}function counterFilter(todos, length) {let startTime = performance.now();while (performance.now() - startTime < 500) {// 在 500 毫秒内不执行任何操作以模拟极慢的代码}return todos.filter((value, index) => index < length)
}

这样一用,哎哟这不是vue的计算属性嘛,蛮有意思蛮有意思

不过useMemo Hook设计来是用于存储运算时间长的计算结果以避免重复渲染的一种优化手段(我自认为是这样

2.useMemo实现组件记忆化 

提到避免重复渲染,useMemo可以结合上文的memo api实现记忆化,解决props传入对象破环记忆化的情况

function Page() {const [name, setName] = useState('Taylor');const [age, setAge] = useState(42);const person = useMemo(() => ({ name, age }),[name, age]);return <Profile person={person} />;
}const Profile = memo(function Profile({ person }) {// ...
});

3.useMemo实现函数记忆化

函数也是一种对象,所以当然能用useMemo实现记忆化 

export default function Page({ productId, referrer }) {const handleSubmit = useMemo(() => {return (orderDetails) => {post('/product/' + productId + '/buy', {referrer,orderDetails});};}, [productId, referrer]);return <Form onSubmit={handleSubmit} />;
}

这样看起来蠢蠢的,函数又套函数!

术业有专攻,记忆函数要使用react专门提供的一个hook,也就是下面要讲到的


(三)useCallback Hook

useCallback – React 中文文档

useCallback 是一个允许你在多次渲染中缓存函数的 React Hook。 

1.用法

const cachedFn = useCallback(fn, dependencies)

用法和useMemo一样,传入两个参数

  • fn:想要缓存的函数。React 将会在初次渲染而非调用时返回该函数。当进行下一次渲染时,如果 dependencies 相比于上一次渲染时没有改变,那么 React 将会返回相同的函数。否则,React 将返回在最新一次渲染中传入的函数,并且将其缓存以便之后使用。React 不会调用此函数,而是返回此函数。你可以自己决定何时调用以及是否调用。

  • dependencies:有关是否更新 fn 的所有响应式值的一个列表。响应式值包括 props、state,和所有在你组件内部直接声明的变量和函数。

  • 返回值:返回想要缓存的函数。

还是用memo那儿的例子:

函数setName本质是对象,即使传入的函数没有改变,Object.is()判断也是false,这样就导致memo失去了记忆化

function App() {const [count, setCount] = useState(0)const [name, setName] = useState('csq')return (<><div>{count}</div><button onClick={()=>{setCount(count+1)}}>加1</button><Name name={name} setName={(name)=>{setName(name)}}></Name></>)
}const Name = memo(function Name(props){console.log('Name组件重新渲染了');return (<><div>{props.name}</div><button onClick={() => { props.setName(props.name+'!') }}>修改名字</button></>)
})

直接上useCallback!

这个useCallback里的依赖数组为空,是因为setName这个函数本身就是不变的,不受变量影响

import { useState, memo, useCallback } from 'react'
function App() {const [count, setCount] = useState(0)const [name, setName] = useState('csq')const setNameCallback = useCallback((name) => { setName(name) },[])return (<><div>{count}</div><button onClick={()=>{setCount(count+1)}}>加1</button><Name name={name} setName={setNameCallback}></Name></>)
}const Name = memo(function Name(props){console.log('Name组件重新渲染了');return (<><div>{props.name}</div><button onClick={() => { props.setName(props.name+'!') }}>修改名字</button></>)
})

(四)总结

今天学了memo、useMemo、useCallback,感觉花太多时间在memo上了,后面两个hook就写的稍微有点水,有点漏洞

以后应该多花点时间在敲代码上,感觉写博客写太久了

memo:用于实现组件记忆化,如果props传递的是对象,那么memo就没意义了

useMemo:立即执行函数返回结果并保存。可以和memo结合使用,传递props不变则不重新渲染;也可以用于存储较花费事件的数据;

useCallback:和memo结合使用,是保存函数但不会执行。

总结来说都是用于性能优化, 不能依靠这些来避免本身代码的问题

相关文章:

React(四)memo、useCallback、useMemo Hook

目录 (一)memo API 1.先想一个情景 2.用法 (1)props传入普通数据类型的情况 (2)props传入对象的情况 (3)props传入函数的情况 (4)使用自定义比较函数 3.什么时候使用memo&#xff1f; (二)useMemo Hook 1.用法 2.useMemo实现组件记忆化 3.useMemo实现函数记忆化 …...

前端介绍及工具环境搭建

前端发展历史 前端介绍 1.什么是前端 前端 &#xff1a;针对浏览器的开发&#xff0c;代码在浏览器中运行后端 &#xff1a;针对服务器的开发&#xff0c;代码在服务器中运行 2.前端的用处&#xff1f; 前端在现代技术环境中扮演着⾄关重要的⻆⾊。 作为⽤户与⽹站或应⽤程…...

uniapp高校二手书交易商城回收系统 微信小程序python+java+node.js+php

每年因为有大量的学生在接受教育&#xff0c;每到大学毕业季的时候&#xff0c;所使用的大量书籍对他们自己来说&#xff0c;很多是没有用&#xff0c;同时由于书籍多和不方便携带&#xff0c;导致很多大学生在毕业时将教材直接丢弃是在校大学生处理已用教材的一种主要方式。然…...

Vue3 图片或视频下载跨域或文件损坏的解决方法

Vue3 图片或视频下载跨域或文件损坏的解决方法 修改跨域配置文件下载方法 修改跨域配置文件 修改vite.config.ts文件proxy里面写跨域地址&#xff0c;如下图&#xff0c;图片地址就是我们要跨域的目标地址&#xff1a; 下载方法 如下就是我取消上面那句后的报错 然后调用两…...

vue2和3区别

Vue2和Vue3在**源码架构、性能提升以及API设计**等方面存在区别。具体分析如下&#xff1a; 1. **源码架构** - **Vue2**&#xff1a;Vue2的源码相对更传统&#xff0c;主要使用Options API来构建组件。这种方式要求开发者在一个对象中定义组件的各种属性&#xff08;如data、m…...

倍福TwinCAT3 PLC编程软件下载安装

1、哪里下载TwinCAT3 链接: Search result | 倍福 中国https://www.beckhoff.com.cn/zh-cn/support/download-finder/search-result/?download_group=97028248下载倍福PLC编程软件需要注册,大家可以提前注册,注册好后就可以开始愉快的下载了 安装前需要注意将各杀毒软件卸…...

Linux一键式管理jar程序执行周期【完整脚本复制可用】

最近由于频繁更新程序&#xff0c;项目又没有自动部署架构&#xff0c;单独执行脚本很麻烦。因此整理了一个脚本&#xff0c;一键式执行。 linux脚本执过程&#xff1a; 1.ps -ef|grep xxx.jar 查询.jar的进程&#xff0c; 2.如果有删除kill -9 进程。 3. 进程删除成功后 nohup…...

设计模式之六大设计原则

文章目录 高内聚低耦合设计原则开闭原则单一职责原则里氏代换原则依赖倒置原则迪米特原则接口隔离原则 高内聚低耦合 提高代码的可读性、可维护性和可扩展性&#xff0c;降低开发和维护的成本&#xff0c;并减少系统的风险 内聚&#xff1a; 内聚表示一个模块内部各个元素之间…...

【iOS】UI学习(一)

UI学习&#xff08;一&#xff09; UILabelUIButtonUIButton事件 UIViewUIView对象的隐藏UIView的层级关系 UIWindowUIViewController定时器与视图对象 UISwitch UILabel UILabel是一种可以显示在屏幕上&#xff0c;显示文字的一种UI。 下面使用代码来演示UILabel的功能&#…...

如何使用Vue和Markdown实现博客功能

创建Vue项目和安装依赖 npm install -g @vue/cli vue create vue-blog cd vue-blog npm install vue-markdown-loader --save-dev配置Vue项目以解析Markdown 在 vue.config.js 文件中添加以下配置: module.exports = {chainWebpack: config => {config...

1初识C#

1、Console安慰 Console.WriteLine("Hello, world!"); // 输出 "Hello, world!" 并换行 Console.WriteLine(123.45); // 输出数字 123.45 并换行 Console.WriteLine("Name: " name); // 输出 "Name: [变量name的值]" 并换行 2、 C…...

查询指定会话免打扰

查询指定用户&#xff08;requestId) 为指定会话&#xff08;targetId&#xff09;的设置的免打扰状态。 提示 该设置为用户级别设置。对应的设置接口详见设置指定会话免打扰。 请求方法 POST&#xff1a; https://数据中心域名/conversation/notification/get.json 频率限…...

Linux-命令

添加权限方法及注意事项: 字母权限法很灵活,无论目录还是文件都可以随意添加删除超级权限 chmod us ... 添加SUID chmod gs ... 添加SGID chmod s ...同时添加SUID和SGID chmod -s ...同时删除SUID和SGID chmod ot ...添加Sticky chmod t ...同上 数字权限表示法添加/删除…...

STM32读写内部FLASH读取芯片id

文章目录 读写内部Flash接线程序编写测试效果补充 读取芯片id代码编写 读写内部Flash 接线 程序编写 首先使用ThisFlash.c来写入flash的基本操作&#xff0c;写入、读取、擦除&#xff0c;然后使用Store.c配合数组来进行主存与flash的交互 ThisFlash.c #include "stm32…...

前端npm打包及报错解决

前端npm install 安装node 下载地址 https://nodejs.org/en/download/prebuilt-binaries 配置环境变量 wget https://nodejs.org/dist/v14.21.3/node-v14.21.3-linux-x64.tar.xz tar xf node-v14.21.3-linux-x64.tar.xz -C /data/ vim /etc/profile export NODE_HOME/data/n…...

vbs执行报错vbs没有文件拓展,双击无法打开

如果看不到文件扩展名需要设置&#xff1a; 无法双击打开vbs 一般为注册表问题 解决办法 将下方代码保存为xxx.reg Windows Registry Editor Version 5.00 [HKEY_CLASSES_ROOT\.VBS] "VBSFile" [HKEY_CLASSES_ROOT.VBS\PersistentHandler] "{5e941d80-bf96-…...

超详细的前后端实战项目(Spring系列加上vue3)前端篇(二)(一步步实现+源码)

好了&#xff0c;兄弟们&#xff0c;继昨天的项目之后&#xff0c;开始继续敲前端代码&#xff0c;完成前端部分 昨天完成了全局页面的代码&#xff0c;和登录页面的代码&#xff0c;不过昨天的代码还有一些需要补充的&#xff0c;这里添加一下 内容补充&#xff1a;在调用登…...

【国产中颖】SH79F9202U单片机驱动LCD段码液晶学习笔记

1. 引言 因新公司之前液晶数显表产品单片机一直用的是 C51单片机(SH79F9202U9)&#xff0c;本人之前没有接触过这款单片机&#xff0c;为了维护老产品不得不重新研究研究这款单片机。 10位ADC LCD的增强型8051微控制器 SH79F9202是一种高速高效率8051可兼容单片机。在同样振…...

人工智能初识

&#x1f31e;欢迎来到人工智能基础的世界 &#x1f308;博客主页&#xff1a;卿云阁 &#x1f48c;欢迎关注&#x1f389;点赞&#x1f44d;收藏⭐️留言&#x1f4dd; &#x1f31f;本文由卿云阁原创&#xff01; &#x1f4c6;首发时间&#xff1a;&#x1f339;2024年5月1…...

【算法刷题day60】Leetcode:84. 柱状图中最大的矩形

文章目录 Leetcode 84. 柱状图中最大的矩形解题思路代码总结 草稿图网站 java的Deque Leetcode 84. 柱状图中最大的矩形 题目&#xff1a;84. 柱状图中最大的矩形 解析&#xff1a;代码随想录解析 解题思路 反方向接雨水。见上一篇文章 代码 class Solution {public int la…...

ThingsBoard物联网网关在智慧城市数据采集中的应用

智慧城市由监控中心、采集网关、前端采集设备、前端感应执行器组成。 为何选用ThingsBoard作为平台 监控中心为物联网平台&#xff0c;该平台包含云计算、大数据、人工智能、物联网、GIS、云安全等主要模块&#xff0c;具备数据采集、数据交换、超大规模计算、数据分析、数据应…...

Java中的打印流PrintStream 和 PrintWriter

PrintStream和PrintWriter在Java中都是用于打印输出的类&#xff0c;但它们之间存在一些明显的区别。以下是关于这两个类的详细解释和比较&#xff1a; PrintStream 基本特性 PrintStream是一个字节打印流&#xff0c;它继承自FilterOutputStream。 主要操作byte流&#xff0…...

【MATLAB源码-第217期】基于matlab的16QAM系统相位偏移估计HOS算法仿真,对比补偿前后的星座图误码率。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 高阶统计量&#xff08;HOS&#xff09;频偏估计算法 高阶统计量&#xff08;Higher Order Statistics, HOS&#xff09;频偏估计算法是一种先进的信号处理技术&#xff0c;广泛应用于现代数字通信系统中&#xff0c;以应对…...

C# CryptoStream流的详解与示例

在当今数字时代&#xff0c;数据安全变得越来越重要。保护敏感信息免受未授权访问是每个开发者的责任。在C#中&#xff0c;使用CryptoStream流可以方便地对数据进行加密和解密。本文将详细介绍C# CryptoStream库的用法、功能以及它如何对数据进行加密和解密。 一、CryptoStrea…...

Kubernetes 之 ReplicaSet

Kubernetes 之 ReplicaSet ReplicaSet 定义 ReplicaSet 是 Kubernetes 中的一种副本控制器&#xff0c;其主要作用是控制其管理的 Pod 的预设副本数量。它会持续监听这些 Pod 的运行状态&#xff0c;在Pod发生故障时执行重启策略&#xff0c;当 Pod 数量减少时会重新启动新的…...

转发和重定向

目录 是什么 转发&#xff08;Forwarding&#xff09; 概念 特点 实现方式 重定向&#xff08;Redirecting&#xff09; 概念 特点 实现方式 转发和重定向区别整理 转发和重定向的适用场景 转发&#xff08;Forwarding&#xff09; 重定向&#xff08;Redirect&am…...

源码部署ELK

目录 资源列表 基础环境 关闭防护墙 关闭内核安全机制 修改主机名 添加hosts映射 一、部署elasticsearch 修改limit限制 部署elasticsearch 修改配置文件 单节点 集群(3台节点集群为例) 启动 二、部署logstash 部署logstash 添加配置文件 启动 三、部署kiban…...

构造+模拟,CF1148C. Crazy Diamond

一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 Problem - 1148C - Codeforces 二、解题报告 1、思路分析 题目提示O(5n)的解法了&#xff0c;事实上我们O(3n)就能解决&#xff0c;关键在于1&#xff0c;n的处理 我们读入数据a[]&#xff0c;代表初始数组…...

CAD二次开发(2)-将直线对象添加到CAD图形文件

1. 准备工作 创建一个类库项目&#xff0c;如下&#xff1a; 2. 分析Line对象 Line类的初始化方法和参数 using Autodesk.AutoCAD.DatabaseServices; Line line new Line();Line 继承Curve 继承Entity 继承DBObject 继承Drawable 继承RXObject 初始化方法有两个&#xf…...

代码随想录二刷 Day05 | 242.有效的字母异位词,349. 两个数组的交集,202. 快乐数,1. 两数之和,454.四数相加II,383. 赎金信

题目与题解 参考资料&#xff1a;哈希表理论基础 Tips&#xff1a; 一般哈希表都是用来快速判断一个元素是否出现集合里哈希表生成原理&#xff1a;先通过哈希函数将变量映射为hashcode&#xff0c;如果二者hashcode相同&#xff0c;再通过哈希碰撞方法&#xff08;拉链法&…...