React几种避免子组件无效刷新的方案
您好,如果喜欢我的文章,可以关注我的公众号「量子前端」,将不定期关注推送前端好文~
前言
一个很常见的场景,React
中父组件和子组件在一起,子组件不依赖于父组件任何数据,但是会一起发生变化。
在探究原理之前,先回忆一下,React
中的Diff算法会将更新前后的两棵虚拟DOM树做对比,但这并不会决定组件是否更新,只会决定是否要复用老的节点。
举个简单的例子:
import { useState } from 'react';const Child = () => {console.log('child render');return null;
};const App = () => {const [name, setName] = useState(1);return (<div onClick={() => setName(2)}><Child /></div>);
};
Child
组件没有接收来自父组件的值,每次点击父组件元素让name
更新,Child
组件会更新吗?答案是会的,你一定会好奇,子组件没有接收任何的props
,为什么也会更新呢?
首先,父组件经过了Diff阶段,会判断Child组件是否发生变化,在本案例中Child
内部的元素结构和状态无任何变化,React
还会对比Child
组件前后的props是否相同,在本案例中,前后props
不相同。
说到这里,你一定忍不住了,我都没传props
,为啥不相同?原因是React
内部对于props
的对比只进行了浅层比较,通过 !== 来判断,这样即使没传props
,每次生成的props
对象都是新的指针,即使为空,也会生成不同的props
空对象,就像这样:
const oldProps = current.memoizedProps; // 更新前老的propsconst newProps = workInProgress.pendingProps; // 待比较更新后的propsif (oldProps !== newProps) {didReceiveUpdate = true; // 标记为发生变化,需要更新
}
那有什么方法可以避免这样的无效更新呢?一共有三种方案。
- 使用
React.memo
,可以指定在Diff时对于被memo
包裹的组件只做浅层比较; - 使用
React.useMemo
或React.useCallback
来包住子组件,让每次更新子组件都为同一个JSX对象,这也props
的比较就会相同; - 将子组件作为
children
来传递;
React.memo
对于方案1,React.memo
的原理其实来源于源码中的shallowEqual
函数,该函数会接收两个对象,分别对应老的props
和新的props
,一共有四种比较策略,如果四种策略都通过,则判定新旧为同一个对象,不做更新,复用老的节点。
- 判断两者是否为同一对象,不是同一对象则返回false;
- 判断两者的值不为
object
或为null
,则返回false; - 对比两者key的数量,不一致则返回false;
- 对比两者key的值是否相同,不一致则返回false;
源码如下:
function shallowEqual(objA: mixed, objB: mixed): boolean {// 一样的对象返回trueif (Object.is(objA, objB)) {return true;}// 不是对象或者为null返回falseif (typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null) {return false;}const keysA = Object.keys(objA);const keysB = Object.keys(objB);// key数量不同返回falseif (keysA.length !== keysB.length) {return false;}// 对应key的值不相同返回falsefor (let i = 0; i < keysA.length; i++) {if (!hasOwnProperty.call(objB, keysA[i]) || !Object.is(objA[keysA[i]], objB[keysA[i]])) {return false;}}return true;
}
可以看到浅比较props
的实现原理很简单,对应着上述四种策略。
React.useMemo & React.useCallBack
对于方案2,如果你不了解React.useMemo
和React.useCallback
,没有关系,先看一下这段代码块:
import { useMemo } from 'react';const Child = () => {console.log('child render');return null;
};const App = () => {const [name, setName] = useState(1);const child = useMemo(() => <Child />, []);return <div onClick={() => setName(2)}>{child}</div>;
};
React.useMemo
接收两个参数,第一个参数为返回值,第二个参数为依赖项,当依赖项数组中的值发生变化,则返回值会重新计算,也就是说第二个依赖项传空数组,则依赖项永远都不会发生变化,则Child
组件经过React.useMemo
包裹后一直不会被React
去计算Diff,就实现了父组件更新,子组件不触发更新。
但对于React.useMemo
的使用,如果传给了子组件的值,但是未声明依赖项,会导致子组件一直不发生变化,就像这样:
import { useMemo } from 'react';const Child = ({ name }) => {console.log('child render');return name;
};const App = () => {const [name, setName] = useState(1);const child = useMemo(() => <Child name={name} />, []);return <div onClick={() => setName(2)}>{child}</div>;
};
像这种情况,父组件将name
传给了子组件,但是由于子组件未声明name
为改变依赖项,因此当name
发生变化,子组件依然会永远返回初始值1,因此对于React.useMemo
的缓存策略在优化时也需要充分考虑意外事故发生。
向上提炼 & 向下移动
对于方案3,可以简单理解成向上提炼和向下移动state,先看一个案例:
const App = () => {const [color, setColor] = useState('red');return (<div><input value={color} onChange={(e) => setColor(e.target.value)} /><p style={{ color }}>Hello, world!</p><Child /></div>);
};
Input
的onChange
事件是一个频繁触发的颜色指示器,一秒会触发上百次,而Child
组件是一个固定渲染不依赖父组件状态的子组件,如何通过状态向下移动的方式来避免Child
组件被渲染呢?
const App = () => {return (<div><Form /><Child /></div>);
};
我们只需要将这段性能消耗大的代码抽离到单独的一个Form
组件中,同时把color
状态单独交给Form
组件去管理,这样App
父组件一直没有发生重渲染,Child
子组件也不会被影响,只有Form
子组件在单独发生交互,这种方案更像是一个状态下移 + 隔离
。
还有一种解法就是状态提升,我们可以把这段性能消耗严重的代码同样单独封装成一个组件,将Child
子组件的内容传递给Form子组件,就像这样:
const Form = ({ children }) => {const [color, setColor] = useState('red');return (<div style={{ color }}><input value={color} onChange={(e) => setColor(e.target.value)} />{children}</div>);
};const App = () => {return (<div><Form><p>Hello, world</p><Child /></Form></div>);
};
其实思路是和状态向下提升是一样的,把性能消耗严重的一部分单独抽离到一个组件中,将相对不期望被影响的一部分通过特定形式渲染,因此Child
子组件在这种情况也不会被重新渲染。
结尾
本文主要记录了博主在日常开发用到比较多的三种优化策略,微笑的细节差带来的优化提升手段,希望对你有帮助哦~
如果喜欢我的文章,可以关注我的公众号「量子前端」,将不定期关注推送前端好文~
相关文章:
React几种避免子组件无效刷新的方案
您好,如果喜欢我的文章,可以关注我的公众号「量子前端」,将不定期关注推送前端好文~ 前言 一个很常见的场景,React中父组件和子组件在一起,子组件不依赖于父组件任何数据,但是会一起发生变化。 在探究原…...

分享亿款好用的PDF编辑工具
所周知,PDF文件是不能够像word/excel/ppt等文件一样,可以被随意编辑的,PDF文件往往只能够被查看,我们无法对它进行编辑,或者对上面的文字进行复制,也不能任意删除上面的页面。但是很多时候,我们…...

AI生成式视频技术来临:Runway Gen-2文本生成视频
Runway Gen-2的官方网站提供了一种文本生成视频的工具。以下是对该工具的介绍: 文本生成视频:Runway Gen-2是一个创新的在线工具,可以将文本转化为视频。用户只需输入文本描述或句子,Runway Gen-2就能自动生成相应的视频内容。这…...
react钩子函数
React组件的生命周期包括多个阶段和方法,用于在组件不同的生命周期时执行特定的操作。以下是React类组件中常见的生命周期方法: 挂载阶段(Mounting Phase): constructor:组件实例化时调用,用于初…...

RISC-V公测平台发布 · 如何在SG2042上玩转k3s
前言 Kubernetes是一个开源的容器管理平台,通过Kubernetes的跨集群管理功能,用户可以方便地进行应用程序的复制、迁移和跨云平台的部署。 而k3s作为Kubernetes的轻量级发行版,相比传统的Kubernetes具有更小的二进制文件大小和更低的资源消耗…...

Linux系统常见小问题
1、新系统在输入命令时,不会自动提示,按上箭头(↑)和下向下箭头(↓)不会匹配之前的输入 以CentOS 为例,可以通过配置 ~/.bashrc 文件来实现按向上箭头显示最相近的命令。以下是具体的实现步骤 …...

WEB:mfw
背景知识 Git泄露 Githack使用 命令执行漏洞 题目 这里页面里有Git,猜测是Git泄露 先用dirsearch扫一下 确实存在.git目录,可以尝试访问一下 使用Githack来下载并恢复.git文件 这里记得使用的时候关闭杀毒软件 结果会自动保存 点进去先看一下flag这个…...

2.4 传统经验光照模型详解
一、光照模型 光照模型(illumination model),也称为明暗模型,用于计算物体某点处的光强(颜色值)。从算法理论基础而言,光照模型分为两类:一种是基于物理理论的,另一种是…...

基于高通QCC5171的对讲机音频数据传输系统设计
一 研发资料准备 二 设计方法 蓝牙连接与配对:使用QCC5171的蓝牙功能,实现设备之间的蓝牙连接和配对。确保设备能够相互识别并建立起稳定的蓝牙连接。 音频采集与处理:将麦克风采集到的音频数据通过QCC5171的ADC(模数转换器&…...
【题解】判断链表中是否有环、链表中环的入口结点
文章目录 判断链表中是否有环链表中环的入口结点 判断链表中是否有环 题目链接:判断链表中是否有环 解题思路1:快慢指针 代码如下: bool hasCycle(ListNode *head) {if(head nullptr) return false;ListNode* fast head;ListNode* slow …...

Pytorch 最全入门介绍,Pytorch入门看这一篇就够了
本文通过详细且实践性的方式介绍了 PyTorch 的使用,包括环境安装、基础知识、张量操作、自动求导机制、神经网络创建、数据处理、模型训练、测试以及模型的保存和加载。 1. Pytorch简介 在这一部分,我们将会对Pytorch做一个简单的介绍,包括它…...
Lambda 表达式的作用域
在Lambda表达式中访问外层作用域和旧版本的匿名对象中的方式类似。你可以直接访问标记了final的外层局部变量,或者实例的字段以及静态变量。 Lambda表达式不会从超类(supertype)中继承任何变量名,也不会引入一个新的作用域。Lambd…...

【portswigger】第二专题-XSS(二)
portswigger 靶场(第二章节)XSS 视频同步更新至bilibili bibi地址 【【portswigger】第二专题-XSS(一前置知识)】 https://www.bilibili.com/video/BV1mp4y157xA/?share_sourcecopy_web 【【portswigger】第二专题-XSSÿ…...

【计算机视觉|人脸建模】3D人脸重建基础知识(入门)
本系列博文为深度学习/计算机视觉论文笔记,转载请注明出处 一、三维重建基础 三维重建(3D Reconstruction)是指根据单视图或者多视图的图像重建三维信息的过程。 1. 常见三维重建技术 人工几何模型仪器采集基于图像的建模描述基于几何建模…...

使用Jetpack Glance创建Android Widget
使用Jetpack Glance创建Android Widget Jetpack Glance发布,让我们使用Google提供的Jetpack Glance创建一个联系人列表小部件。 https://developer.android.com/jetpack/compose/glance 什么是Glance? Jetpack Glance是一个使用Kotlin API创建小型、轻…...

【MyBatis 学习三】子段不一致问题 多表查询 动态SQL
目录 一、解决Java实体类属性与数据库表字段不一致问题 🌷现象1:显示字段不对应:使用ResultType查询结果为null; 🌷解决办法:字段不对应:使用ResultMap解决。 二、数据库的多表查询 &#…...

15. Spring AOP 的实现原理 代理模式
目录 1. 代理模式 2. 静态代理 3. 动态代理 3.1 JDK 动态代理 3.2 CGLIB 动态代理 4. JDK 动态代理和 CGLIB 动态代理对比 5. Spring代理选择 6. Spring AOP 实现原理 6.1 织入 7. JDK 动态代理实现 8. CGLIB 动态代理实现 9. 总结 1. 代理模式 代理模式…...
死锁产生的原因以及解决方案
一.原因: 1.使用互斥锁. 2.除非主动释放,负责不能被抢占. 3.占用一把锁不释放,等待其它锁资源(保持现状). 4.锁形成环路. 二.解决方案: 给锁编号,上锁的时候从小到大依次上锁,譬如如果一个线程要上1号和2号两把锁,如果1号锁被占用,不能上2号锁,等其它线程释放1号锁资源后…...

【构造】CF1758 D
Problem - D - Codeforces 题意: 思路: 如果需要构造一个和为定值的序列,那么考虑n-d,n-d1,.....nd-1,nd这种形式 如果要保证不能重复,那么先考虑一个排列,然后在排列上操作 如果根据小数据构造出了一些简单情形&a…...

【腾讯云 Cloud Studio 实战训练营】永不宕机的IDE,Coding Everywhere
【腾讯云 Cloud Studio 实战训练营】永不宕机的IDE,随时随地写代码! 写在最前视频讲解:Cloud Studio活动简介何为腾讯云 Cloud Studio?Cloud Studio简介免费试用,上手无忧Cloud Studio 特点及优势云端开发多种预制环境可选metawo…...

调用支付宝接口响应40004 SYSTEM_ERROR问题排查
在对接支付宝API的时候,遇到了一些问题,记录一下排查过程。 Body:{"datadigital_fincloud_generalsaas_face_certify_initialize_response":{"msg":"Business Failed","code":"40004","sub_msg…...
java调用dll出现unsatisfiedLinkError以及JNA和JNI的区别
UnsatisfiedLinkError 在对接硬件设备中,我们会遇到使用 java 调用 dll文件 的情况,此时大概率出现UnsatisfiedLinkError链接错误,原因可能有如下几种 类名错误包名错误方法名参数错误使用 JNI 协议调用,结果 dll 未实现 JNI 协…...

UDP(Echoserver)
网络命令 Ping 命令 检测网络是否连通 使用方法: ping -c 次数 网址ping -c 3 www.baidu.comnetstat 命令 netstat 是一个用来查看网络状态的重要工具. 语法:netstat [选项] 功能:查看网络状态 常用选项: n 拒绝显示别名&#…...
React Native在HarmonyOS 5.0阅读类应用开发中的实践
一、技术选型背景 随着HarmonyOS 5.0对Web兼容层的增强,React Native作为跨平台框架可通过重新编译ArkTS组件实现85%以上的代码复用率。阅读类应用具有UI复杂度低、数据流清晰的特点。 二、核心实现方案 1. 环境配置 (1)使用React Native…...

智能在线客服平台:数字化时代企业连接用户的 AI 中枢
随着互联网技术的飞速发展,消费者期望能够随时随地与企业进行交流。在线客服平台作为连接企业与客户的重要桥梁,不仅优化了客户体验,还提升了企业的服务效率和市场竞争力。本文将探讨在线客服平台的重要性、技术进展、实际应用,并…...

C++ Visual Studio 2017厂商给的源码没有.sln文件 易兆微芯片下载工具加开机动画下载。
1.先用Visual Studio 2017打开Yichip YC31xx loader.vcxproj,再用Visual Studio 2022打开。再保侟就有.sln文件了。 易兆微芯片下载工具加开机动画下载 ExtraDownloadFile1Info.\logo.bin|0|0|10D2000|0 MFC应用兼容CMD 在BOOL CYichipYC31xxloaderDlg::OnIni…...

视频行为标注工具BehaviLabel(源码+使用介绍+Windows.Exe版本)
前言: 最近在做行为检测相关的模型,用的是时空图卷积网络(STGCN),但原有kinetic-400数据集数据质量较低,需要进行细粒度的标注,同时粗略搜了下已有开源工具基本都集中于图像分割这块,…...
虚拟电厂发展三大趋势:市场化、技术主导、车网互联
市场化:从政策驱动到多元盈利 政策全面赋能 2025年4月,国家发改委、能源局发布《关于加快推进虚拟电厂发展的指导意见》,首次明确虚拟电厂为“独立市场主体”,提出硬性目标:2027年全国调节能力≥2000万千瓦࿰…...

【JVM】Java虚拟机(二)——垃圾回收
目录 一、如何判断对象可以回收 (一)引用计数法 (二)可达性分析算法 二、垃圾回收算法 (一)标记清除 (二)标记整理 (三)复制 (四ÿ…...

水泥厂自动化升级利器:Devicenet转Modbus rtu协议转换网关
在水泥厂的生产流程中,工业自动化网关起着至关重要的作用,尤其是JH-DVN-RTU疆鸿智能Devicenet转Modbus rtu协议转换网关,为水泥厂实现高效生产与精准控制提供了有力支持。 水泥厂设备众多,其中不少设备采用Devicenet协议。Devicen…...