正确使用React组件缓存
简介
正常来讲的话当我们点击组件的时候,该组件以及该组件的子组件都会重新渲染,但是如何避免子组件重新渲染呢,我们经常用memo来解决
React.memo配合useCallback缓存组件
- 父组件没有传props
const Index = ()=> {console.log('子组件刷新了');return (<div>这是子组件</div>)
}
//这里我们用react.memo对组件进行包裹,包裹一次之后react在render的过程中不会给该fiber打上更新的tag
//从而跳过更新,这个原理其实就是react.memo的第二个参数上,如果react.memo第二个参数不传递,react回默
//认给我们补充上第二个参数的逻辑,其中逻辑就是浅比较Index组件的props参数,如果相等的话默认第二个参数返
//回true,组件就会缓存了,如果不相等的话就会返回false组件就会重新打上更新的tag然后重新渲染。
const MemoIndex = React.memo(Index);const App = ()=>{const [state, setState] = useState(0);return (<div className="App"><button onClick={()=>setState(state+1)}>点我看看子组件刷新了吗</button><MemoIndex/></div>);
}
- 父组件传了state
const Index = ()=> {console.log('子组件刷新了');return (<div>这是子组件</div>)
}
//其实这Index组件不会更新的,react检测到我们不传递第二个参数的话,会把之前的props拆出来,和现在的
//props做比较 发现pre.next === cur.next 然后回返回true组件就会缓存了
const MemoIndex = React.memo(Index);
//这行代码就相当这样的代码
const MemoIndex = React.memo(Index, (pre, cur)=> {//这样写的就比较简单了,因为这是是针对于当前的demo来说的。react比较的代码逻辑比较复杂,因为react//需要考虑到多种情况,props中参数可能多一个少一个的情况,所以react默认提供的代码比较复杂if(pre.name === cur.name) {return true;}return false;
});const App = ()=>{const [state, setState] = useState(0);return (<div className="App"><button onClick={()=>setState(state+1)}>点我看看子组件刷新了吗</button><MemoIndex name={0}/></div>);
}
- 父组件传了函数
const Index = ()=> {console.log('子组件刷新了');return (<div>这是子组件</div>)
}
const MemoIndex = React.memo(Index);const App = ()=>{const [state, setState] = useState(0);const func = ()=> {};return (<div className="App">//这时候子组件会不会刷新呢,有的同学可能说不会因为浅比较发现pre.func === cur.func 返回//true所以不会刷新,但是其实是会刷新的,因为APP组件中触发了setState之后App组件重新渲染,也//就是相当于执行了App()这个方法,所以里面func的指向地址发生了变化,所以pre.func !== //cur.func 子组件会重新渲染,<button onClick={()=>setState(state+1)}>点我看看子组件刷新了吗</button><MemoIndex func={func}/></div>);
}
使用useCallback缓存函数
const Index = ()=> {console.log('子组件刷新了');return (<div>这是子组件</div>)
}const MemoIndex = React.memo(Index);const App = ()=>{const [state, setState] = useState(0);//这里我们用了useCallback,useCallback主要是缓存我们当前的函数,如果我们第二个参数传递空数组的话//他的地址不会改变,如果我们第二个参数传递的是一个变量,这个变量发生变化他的地址就会发生变化。所以这//和useEffect的第二个参数是一样的,但是请注意不要滥用useCallback的第二个参数。如果第二个参数滥用//会拿到我们之前的值。我们看下一个示例就知道了const func = useCallback(()=> {}, [])return (<div className="App"><button onClick={()=>setState(state+1)}>点我看看子组件刷新了吗</button><MemoIndex func={func}/></div>);
}
使用useMemo缓存组件
useMemo不仅可以缓存变量,函数还可以缓存组件
const Index = (props)=> {console.log('子组件刷新了');return (<div>这是子组件</div>)
}const App = ()=>{const [state, setState] = useState(0);//使用useMemo也要和useCallback一样特别注意第二个参数,因为他有可能导致我们拿不到最新的数据解决//解决方案就和useCallback的一样,简单来说套用react官方的话就是请确保数组中包含了所有外部作用域//中会随时间变化并且在useMemo中使用的变量都要放到第二个参数中。const Component = useMemo(()=><Index/>, []);return (<div className="App"><button onClick={()=>setState(state+1)}>点我看看子组件刷新了吗</button>{Component}</div>);
}
利用props.children缓存组件
这样在Index组件re-render的时候,由于App(父组件)中的组件没有变化,所以拿到的children依然是上一次的(没有发生变化的)所以children部分不会re-render。
const Index = (props)=> {const [state, setState] = useState(0);return (<div>//当这个按钮点击之后我们发现Children组件并不重新刷新了,其实原理我理解的是react帮我们做了一层//处理当渲染前与渲染后两个组件的引用地址一样他就会放弃render,当然这是我的猜测,这个的话之后//我看到源码的时候会和大家讲一下在补充一下。<button onClick={()=>setState(state+1)}>点我我看看子组件刷新不刷新</button>{props.children}</div>)
};
const Children = ()=> {return (<div>{console.log('子组件刷新了')}这是children组件</div>)
}const App = ()=>{return (<div className="App"><Index><Children/></Index></div>);
}
注意事项
- useCallBack不是每个函数都需要使用!不要滥用useCallback
const Index = (props)=> {console.log('子组件刷新了');return (<div>//点击这个按钮之前先点击App组件下面的按钮,让state变大,然后在点击这个按钮看看state是啥//我们发现state一直是一个0。这是为什么呢,因为很简单我们之前讲了useCallback第二个和//useEffect的第二个参数是一样的,因为我们传递的是空数组说以useCallback一直拿到的是最原始的//值,所以会造成这个问题,我们写代码的时候千万要注意第二个参数,只要useCallback需要什//值我们就在第二个参数传递什么值,这样才可以确保我们拿到的是最新的值。同样的里面如果不需要一些//参数的话我们也不要把这些参数加到第二个参数上面否则会出现func的地址多次改变。<button onClick={props.func}>点我看看state是啥</button>这是子组件</div>)
}
const MemoIndex = React.memo(Index);const App = ()=>{const [state, setState] = useState(0);const func = useCallback(()=> {console.log(state);}, [])return (<div className="App"><button onClick={()=>setState(state+1)}>点我看看子组件刷新了吗</button><MemoIndex func={func}/></div>);
}
- useCallBack是一个缓存工具没错。但实际上他并不能阻止函数都重现构建。
示例:
大家看上方这种结构的组件,Com组件中包含了fun1和fun2两个函数。
是不是认为当Com组件重新渲染的时候,只有fun2(没有使用useCallBack的函数)函数会被重新构建,而fun1(使用了useCallBack的函数)函数不会被重新构建。
实际上,被useCallBack包裹了的函数也会被重新构建并当成useCallBack函数的实参传入。
useCallBack的本质工作不是在依赖不变的情况下阻止函数创建,而是在依赖不变的情况下不返回新的函数地址而返回旧的函数地址。不论是否使用useCallBack都无法阻止组件render时函数的重新创建!!
每一个被useCallBack的函数都将被加入useCallBack内部的管理队列。而当我们大量使用useCallBack的时候,管理队列中的函数会非常之多,任何一个使用了useCallBack的组件重新渲染的时候都需要去遍历useCallBack内部所有被管理的函数找到需要校验依赖是否改变的函数并进行校验。
在以上这个过程中,寻找指定函数需要性能,校验也需要性能。所以,滥用useCallBack不但不能阻止函数重新构建还会增加“寻找指定函数和校验依赖是否改变”这两个功能,为项目增添不必要的负担。
//Com组件
const Com = () => {//示例1包裹了useCallBack的函数const fun1 = useCallBack(() => {console.log('示例一函数');...},[])//示例2没有包裹useCallBack的函数const fun2 = () => {console.log('示例二函数');...}return <div></div>
}
不要过度缓存组件
其实不必过度优化代码 react官方没有帮你做 其实也证明了 如果你的代码没有明显的卡顿 你自己去做优化 可能造成负优化
优化的手段一般都是针对组件本身比较复杂且数据量大每次re-render都会造成卡顿的情况下才去做的,没有太明显的卡顿出现时没必要做这些优化。而且在使用memo前其实也有手段去规避这些无效的re-render,比如将组件粒度划分的更细一些
参考文章
useCallBack你真的知道怎么用吗。
相关文章:
正确使用React组件缓存
简介 正常来讲的话当我们点击组件的时候,该组件以及该组件的子组件都会重新渲染,但是如何避免子组件重新渲染呢,我们经常用memo来解决 React.memo配合useCallback缓存组件 父组件没有传props const Index ()> {console.log(子组件刷新…...
AMEYA360:大唐恩智浦荣获 2023芯向亦庄 “汽车芯片50强”
2023年11月28日,由北京市科学技术委员会和北京市经济和信息化局指导、北京经济技术开发区管理委员会主办、盖世汽车协办的“芯向亦庄”汽车芯片大赛在北京亦庄成功闭幕。 在本次大赛中 大唐恩智浦的 电池管理芯片DNB1168 (应用于新能源汽车BMS系统) 凭卓越的性能及高…...
在Arch Linux上安装yay
有点麻烦。 准备 # pacman -Syu # pacman -S --needed base-devel git 变身为普通用户 不能使用root下载代码。所以要变身为普通用户: # sueradd tsit # su tsit 下载代码 $ git clone https://aur.archlinux.org/yay.git 编译安装 $ cd yay $ makepkg -si…...
PHP案例:探究MySQL应用开发喜好的网络调查
文章目录 一、知识准备(一)数据库与表的创建(二)录入调查选项(三)创建问卷页面(四)处理投票数据(五)显示调查结果二、实现步骤(一)创建数据库与表(二)录入若干调查选项(三)创建问卷页面(四)创建调查结果页面(五)体验运行结果(六)查看最终生成的HTML代码很…...
力扣第374场周赛题解
这一场周赛的题目是比较难的一次,写了1个多小时就写了两个题目。 首先第一题: 纯水题,遍历然后进行一下判断就可以解决了。这边就不放代码了。 第二题: 这个题目,我觉得难度非常大,其实代码量也不大都是很…...
Linux Docker 安装Nginx
1.21、查看可用的Nginx版本 访问Nginx镜像库地址:https://hub.docker.com/_/nginx 2、拉取指定版本的Nginx镜像 docker pull nginx:latest #安装最新版 docker pull nginx:1.25.3 #安装指定版本的Nginx 3、查看本地镜像 docker images 4、根据镜像创建并运行…...
鸿蒙应用开发(二)环境搭建
开发流程 IDE下载 首先下载HUAWEI DevEco Studio,介绍首次启动DevEco Studio的配置向导: 运行已安装的DevEco Studio,首次使用,请选择Do not import settings,单击OK。安装Node.js与ohpm。node.js 是基于 V8 引擎构…...
在 Qt Creator 中编写 Doxygen 风格的注释
2023年12月10日,周日上午 如何生成Doxygen 风格的注释 在需要Doxygen 风格注释的函数上方输入 /**,然后按下 Enter 键。Qt Creator 将自动为你生成一个注释模板。 输入,Qt Creator会自动帮你补全Doxygen标签 不得不说,写了Doxyge…...
NSS [NSSCTF 2022 Spring Recruit]babyphp
NSS [NSSCTF 2022 Spring Recruit]babyphp 考点:PHP特性 开局源码直接裸奔 <?php highlight_file(__FILE__); include_once(flag.php);if(isset($_POST[a])&&!preg_match(/[0-9]/,$_POST[a])&&intval($_POST[a])){if(isset($_POST[b1])&&…...
ToolkenGPT:用大量工具增强LLM
深度学习自然语言处理 原创作者:cola 用外部工具增强大型语言模型(LLM)已经成为解决复杂问题的一种方法。然而,用样例数据对LLM进行微调的传统方法,可能既昂贵又局限于一组预定义的工具。最近的上下文学习范式缓解了这一问题,但有…...
2022蓝桥杯c组求和
题目名字 求和 题目链接 题意 输入的每个数都要两两相乘,然后再加起来,求最后总和; 思路 每个数乘这个数的前缀和即可 算法一:前缀和 实现步骤 先把前缀和写出来再写for循环每个数都乘以自己的前缀和; 实现步骤 直接…...
Altium Designer学习笔记11
画一个LED的封装: 使用这个SMD5050的封装。 我们先看下这个芯片的功能说明: 5050贴片式发光二极管: XL-5050 是单线传输的三通道LED驱动控制芯片,采用的是单极性归零码协议。 数据再生模块的功能,自动将级联输出的数…...
TTS | 2019~2023年最新增强/生成情绪的语音合成调研(20231211更新版)
本博客主要是 增强/生成情绪的语音合成调研,论文按照时间顺序排列,且有些论文为期刊会议论文,有的是arxiv论文,在本文中,标识如下: 【🔊ICML 】【✨Interspeech 】【🫧ICASSP】 20…...
搜狗输入法v模式 | 爱莉希雅皮肤
搜狗输入法v模式 | 爱莉希雅皮肤 前言爱莉希雅皮肤v模式 前言 搜狗输入法有v模式,v模式是一个转换和计算的功能组合。拥有数字转换、日期转换、算式计算、函数计算等功能。本文介绍如何使用v模式,并附赠一个爱莉希雅的皮肤,可通过百度网盘下…...
2023年阿里云云栖大会-核心PPT资料下载
一、峰会简介 历经14届的云栖大会,是云计算产业的建设者、推动者、见证者。2023云栖大会以“科技、国际、年轻”为基调,以“计算,为了无法计算的价值”为主题,发挥科技平台汇聚作用,与云计算全产业链上下游的先锋代表…...
JavaScript实战:制作一个待办事项列表应用
JavaScript实战:制作一个待办事项列表应用 引言 在本教程中,我们将一步步创建一个简单的待办事项列表应用,这不仅会帮助你学习基本的JavaScript编程概念,还会教会你如何处理事件以及操作DOM。这个项目是面向初学者的,…...
4面百度软件测试工程师的面试经验总结
没有绝对的天才,只有持续不断的付出。对于我们每一个平凡人来说,改变命运只能依靠努力幸运,但如果你不够幸运,那就只能拉高努力的占比。 2023年7月,我有幸成为了百度的一名测试工程师,从外包辞职了历经1000…...
textarea文本框回车enter的时候自动提交表单,根据内容自动高度
切图网近期一个bootstrap5仿chatgpt页面的项目遇到的,textarea文本框回车enter的时候自动提交表单,根据内容自动高度,代码如下,亲测可用。 <textarea placeholder"Message ChatGPT…" name"" rows"&q…...
dubbo框架技术文档-《spring-boot整合dubbo框架搭建+配置文件》框架的本地基础搭建
阿丹: 目前流行的微服务更多的就是dubbo和springcould微服务。之前阿丹没有出过dubbo相关的文章,因为之前接触springcould的微服务概念比较多一点,但是相对于springcould来说,springcould服务之间的调用是大多是使用了nacos&#…...
中通快递单号查询入口,将指定某天签收的单号筛选出来
批量查询中通快递单号的物流信息,将指定某天签收的单号筛选出来。 所需工具: 一个【快递批量查询高手】软件 中通快递单号若干 操作步骤: 步骤1:运行【快递批量查询高手】软件,并登录 步骤2:点击主界面左…...
快速部署Python3.10环境:Miniconda镜像实战教学
快速部署Python3.10环境:Miniconda镜像实战教学 1. 为什么选择Miniconda搭建Python环境? 在Python开发中,最让人头疼的问题之一就是环境管理。不同项目可能需要不同版本的Python和依赖库,直接安装会导致版本冲突。Miniconda提供…...
【个人推荐】一些好用的录音转写工具
因为助教课备课的缘故,需要录制讲座的音频以整理知识点。一次讲座的音频内容很长,即使3x速快进播放依然很耗费时间,因此录音转写的需求浮现了出来。于是闲暇之余探索了下市面上的录音转写工具,浅浅记录下体验。 下面主要推荐三款…...
国产N32芯片开发避坑指南:J-Link在Keil中的特殊配置(含Cortex-M0配置模板)
国产N32芯片开发实战:J-Link调试配置深度解析与Keil环境优化 在国产MCU生态快速崛起的背景下,N32系列芯片凭借优异的性价比和本土化服务优势,正逐步成为工程师替代进口方案的新选择。然而,从传统ST芯片转向国产平台时,…...
基于MCGS嵌入版7.7的全自动洗车机组态仿真程序编写与流程图详解
MCGS洗车程序 MCGS嵌入版7.7组态仿真程序 全自动洗车机,脚本程序编写 有完整的流程图"这洗车机PLC程序怎么又卡在喷淋环节了?"凌晨两点的工控车间里,我盯着MCGS嵌入版的仿真界面直挠头。全自动洗车机的脚本调试真是个磨人的小妖精&…...
Phi-4-mini-reasoning开源模型优势:轻量级+高精度+低GPU资源占用实测
Phi-4-mini-reasoning开源模型优势:轻量级高精度低GPU资源占用实测 1. 模型概述 Phi-4-mini-reasoning是一款专注于推理任务的文本生成模型,特别擅长处理数学题、逻辑题、多步分析和简洁结论输出。与通用聊天模型不同,它采用了"题目输…...
OpenCV轮廓匹配避坑指南:用cv2.matchShapes做形状识别,为什么你的结果总不准?
OpenCV轮廓匹配避坑指南:为什么你的cv2.matchShapes结果总是不准? 在工业质检、医疗影像分析等场景中,形状匹配的准确性直接影响着整个系统的可靠性。许多开发者在使用OpenCV的cv2.matchShapes函数时,明明按照官方文档操作&#x…...
4大场景:如何用ReplaceItems脚本实现Illustrator批量设计元素智能替换
4大场景:如何用ReplaceItems脚本实现Illustrator批量设计元素智能替换 【免费下载链接】illustrator-scripts Adobe Illustrator scripts 项目地址: https://gitcode.com/gh_mirrors/il/illustrator-scripts 在UI设计和品牌视觉开发过程中,设计师…...
LongCat-Video:AI视频生成技术的范式突破与实践指南
LongCat-Video:AI视频生成技术的范式突破与实践指南 【免费下载链接】LongCat-Video 项目地址: https://ai.gitcode.com/hf_mirrors/meituan-longcat/LongCat-Video 在数字内容创作领域,AI视频生成技术正经历从实验性探索到产业化应用的关键转折…...
树莓派新手必看:保姆级vim安装与配置指南(含国内源切换和常见报错解决)
树莓派新手必看:保姆级vim安装与配置指南(含国内源切换和常见报错解决) 第一次接触树莓派的新手们,面对命令行操作往往既兴奋又忐忑。作为Linux系统中最强大的文本编辑器之一,vim的高效与灵活令人向往,但初…...
手把手教你从Docker中提取Milvus二进制文件并配置集群环境
深度解析:从Docker镜像提取Milvus二进制文件的完整实践指南 在向量数据库领域,Milvus凭借其出色的性能和可扩展性已经成为众多AI应用的首选基础设施。虽然官方推荐使用Docker进行部署,但在生产环境中,直接使用二进制文件部署往往…...
