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

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; // 标记为发生变化,需要更新
}

那有什么方法可以避免这样的无效更新呢?一共有三种方案。

  1. 使用React.memo,可以指定在Diff时对于被memo包裹的组件只做浅层比较;
  2. 使用React.useMemoReact.useCallback来包住子组件,让每次更新子组件都为同一个JSX对象,这也props的比较就会相同;
  3. 将子组件作为children来传递;

React.memo

对于方案1,React.memo的原理其实来源于源码中的shallowEqual函数,该函数会接收两个对象,分别对应老的props和新的props,一共有四种比较策略,如果四种策略都通过,则判定新旧为同一个对象,不做更新,复用老的节点。

  1. 判断两者是否为同一对象,不是同一对象则返回false;
  2. 判断两者的值不为object或为null,则返回false;
  3. 对比两者key的数量,不一致则返回false;
  4. 对比两者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.useMemoReact.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>);
};

InputonChange事件是一个频繁触发的颜色指示器,一秒会触发上百次,而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几种避免子组件无效刷新的方案

您好&#xff0c;如果喜欢我的文章&#xff0c;可以关注我的公众号「量子前端」&#xff0c;将不定期关注推送前端好文~ 前言 一个很常见的场景&#xff0c;React中父组件和子组件在一起&#xff0c;子组件不依赖于父组件任何数据&#xff0c;但是会一起发生变化。 在探究原…...

分享亿款好用的PDF编辑工具

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

AI生成式视频技术来临:Runway Gen-2文本生成视频

Runway Gen-2的官方网站提供了一种文本生成视频的工具。以下是对该工具的介绍&#xff1a; 文本生成视频&#xff1a;Runway Gen-2是一个创新的在线工具&#xff0c;可以将文本转化为视频。用户只需输入文本描述或句子&#xff0c;Runway Gen-2就能自动生成相应的视频内容。这…...

react钩子函数

React组件的生命周期包括多个阶段和方法&#xff0c;用于在组件不同的生命周期时执行特定的操作。以下是React类组件中常见的生命周期方法&#xff1a; 挂载阶段&#xff08;Mounting Phase&#xff09;&#xff1a; constructor&#xff1a;组件实例化时调用&#xff0c;用于初…...

RISC-V公测平台发布 · 如何在SG2042上玩转k3s

前言 Kubernetes是一个开源的容器管理平台&#xff0c;通过Kubernetes的跨集群管理功能&#xff0c;用户可以方便地进行应用程序的复制、迁移和跨云平台的部署。 而k3s作为Kubernetes的轻量级发行版&#xff0c;相比传统的Kubernetes具有更小的二进制文件大小和更低的资源消耗…...

Linux系统常见小问题

1、新系统在输入命令时&#xff0c;不会自动提示&#xff0c;按上箭头&#xff08;↑&#xff09;和下向下箭头&#xff08;↓&#xff09;不会匹配之前的输入 以CentOS 为例&#xff0c;可以通过配置 ~/.bashrc 文件来实现按向上箭头显示最相近的命令。以下是具体的实现步骤 …...

WEB:mfw

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

2.4 传统经验光照模型详解

一、光照模型 光照模型&#xff08;illumination model&#xff09;&#xff0c;也称为明暗模型&#xff0c;用于计算物体某点处的光强&#xff08;颜色值&#xff09;。从算法理论基础而言&#xff0c;光照模型分为两类&#xff1a;一种是基于物理理论的&#xff0c;另一种是…...

基于高通QCC5171的对讲机音频数据传输系统设计

一 研发资料准备 二 设计方法 蓝牙连接与配对&#xff1a;使用QCC5171的蓝牙功能&#xff0c;实现设备之间的蓝牙连接和配对。确保设备能够相互识别并建立起稳定的蓝牙连接。 音频采集与处理&#xff1a;将麦克风采集到的音频数据通过QCC5171的ADC&#xff08;模数转换器&…...

【题解】判断链表中是否有环、链表中环的入口结点

文章目录 判断链表中是否有环链表中环的入口结点 判断链表中是否有环 题目链接&#xff1a;判断链表中是否有环 解题思路1&#xff1a;快慢指针 代码如下&#xff1a; bool hasCycle(ListNode *head) {if(head nullptr) return false;ListNode* fast head;ListNode* slow …...

Pytorch 最全入门介绍,Pytorch入门看这一篇就够了

本文通过详细且实践性的方式介绍了 PyTorch 的使用&#xff0c;包括环境安装、基础知识、张量操作、自动求导机制、神经网络创建、数据处理、模型训练、测试以及模型的保存和加载。 1. Pytorch简介 在这一部分&#xff0c;我们将会对Pytorch做一个简单的介绍&#xff0c;包括它…...

Lambda 表达式的作用域

在Lambda表达式中访问外层作用域和旧版本的匿名对象中的方式类似。你可以直接访问标记了final的外层局部变量&#xff0c;或者实例的字段以及静态变量。 Lambda表达式不会从超类&#xff08;supertype&#xff09;中继承任何变量名&#xff0c;也不会引入一个新的作用域。Lambd…...

【portswigger】第二专题-XSS(二)

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

【计算机视觉|人脸建模】3D人脸重建基础知识(入门)

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

使用Jetpack Glance创建Android Widget

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

【MyBatis 学习三】子段不一致问题 多表查询 动态SQL

目录 一、解决Java实体类属性与数据库表字段不一致问题 &#x1f337;现象1&#xff1a;显示字段不对应&#xff1a;使用ResultType查询结果为null&#xff1b; &#x1f337;解决办法&#xff1a;字段不对应&#xff1a;使用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. 代理模式 代理模式&#xf…...

死锁产生的原因以及解决方案

一.原因: 1.使用互斥锁. 2.除非主动释放,负责不能被抢占. 3.占用一把锁不释放,等待其它锁资源(保持现状). 4.锁形成环路. 二.解决方案: 给锁编号,上锁的时候从小到大依次上锁,譬如如果一个线程要上1号和2号两把锁,如果1号锁被占用,不能上2号锁,等其它线程释放1号锁资源后…...

【构造】CF1758 D

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

【腾讯云 Cloud Studio 实战训练营】永不宕机的IDE,Coding Everywhere

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

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…...

python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)

更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...

视频行为标注工具BehaviLabel(源码+使用介绍+Windows.Exe版本)

前言&#xff1a; 最近在做行为检测相关的模型&#xff0c;用的是时空图卷积网络&#xff08;STGCN&#xff09;&#xff0c;但原有kinetic-400数据集数据质量较低&#xff0c;需要进行细粒度的标注&#xff0c;同时粗略搜了下已有开源工具基本都集中于图像分割这块&#xff0c…...

论文阅读笔记——Muffin: Testing Deep Learning Libraries via Neural Architecture Fuzzing

Muffin 论文 现有方法 CRADLE 和 LEMON&#xff0c;依赖模型推理阶段输出进行差分测试&#xff0c;但在训练阶段是不可行的&#xff0c;因为训练阶段直到最后才有固定输出&#xff0c;中间过程是不断变化的。API 库覆盖低&#xff0c;因为各个 API 都是在各种具体场景下使用。…...

Python 高效图像帧提取与视频编码:实战指南

Python 高效图像帧提取与视频编码:实战指南 在音视频处理领域,图像帧提取与视频编码是基础但极具挑战性的任务。Python 结合强大的第三方库(如 OpenCV、FFmpeg、PyAV),可以高效处理视频流,实现快速帧提取、压缩编码等关键功能。本文将深入介绍如何优化这些流程,提高处理…...

Kubernetes 节点自动伸缩(Cluster Autoscaler)原理与实践

在 Kubernetes 集群中&#xff0c;如何在保障应用高可用的同时有效地管理资源&#xff0c;一直是运维人员和开发者关注的重点。随着微服务架构的普及&#xff0c;集群内各个服务的负载波动日趋明显&#xff0c;传统的手动扩缩容方式已无法满足实时性和弹性需求。 Cluster Auto…...

WebRTC调研

WebRTC是什么&#xff0c;为什么&#xff0c;如何使用 WebRTC有什么优势 WebRTC Architecture Amazon KVS WebRTC 其它厂商WebRTC 海康门禁WebRTC 海康门禁其他界面整理 威视通WebRTC 局域网 Google浏览器 Microsoft Edge 公网 RTSP RTMP NVR ONVIF SIP SRT WebRTC协…...

CSS3相关知识点

CSS3相关知识点 CSS3私有前缀私有前缀私有前缀存在的意义常见浏览器的私有前缀 CSS3基本语法CSS3 新增长度单位CSS3 新增颜色设置方式CSS3 新增选择器CSS3 新增盒模型相关属性box-sizing 怪异盒模型resize调整盒子大小box-shadow 盒子阴影opacity 不透明度 CSS3 新增背景属性ba…...

虚幻基础:角色旋转

能帮到你的话&#xff0c;就给个赞吧 &#x1f618; 文章目录 移动组件使用控制器所需旋转&#xff1a;组件 使用 控制器旋转将旋转朝向运动&#xff1a;组件 使用 移动方向旋转 控制器旋转和移动旋转 缺点移动旋转&#xff1a;必须移动才能旋转&#xff0c;不移动不旋转控制器…...

二维数组 行列混淆区分 js

二维数组定义 行 row&#xff1a;是“横着的一整行” 列 column&#xff1a;是“竖着的一整列” 在 JavaScript 里访问二维数组 grid[i][j] 表示 第i行第j列的元素 let grid [[1, 2, 3], // 第0行[4, 5, 6], // 第1行[7, 8, 9] // 第2行 ];// grid[i][j] 表示 第i行第j列的…...