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

React中每次渲染都会传入一个新的props.children到子组件?

传入props.children后, 为什么会导致组件的重新渲染?

问题描述

在 react 中, 我想要对组件的渲染进行优化, 遇到了一个非常意思的问题, 当我向一个组件中传入了 props.children 之后, 每次父组件重新渲染都会导致这个组件的重新渲染; 它看起来的表现就像是被memo包裹的组件, props和自身状态未发生变化, 组件却重新渲染了; 下面我写了一个demo, 一起来看看这个问题吧:

父组件App中引入了一个Home组件:

import Home from "./pages/Home";
import { useState } from "react";function App() {const [count, setCount] = useState(0);console.log("App is render");return (<div className="App">{count}<button onClick={() => setCount(count + 1)}>Increment</button><Home></Home></div>);
}

使用 memo 包裹 Home 子组件, 同时 Home 组件可以接收一个 props.children 展示传入到 Home 中的组件, 如下:

import React, { memo } from "react";const Home = memo((props) => {console.log("Home is render");return (<div>Home{props.children}</div>);
});export default Home;

目前在 App 组件中, 没有向 Home 组件中传入 props.children, 此时第一次加载时 App 组件和 Home 组件都会重新渲染, 当我们点击 Increment 按钮让 count 的值变化时, App 组件重新渲染, 由于 Home 组件被 memo 包裹, 当 Home 组件的 props 和自身状态未发生变化时, 组件不进行重新渲染, 目前也正是我们所期望的这样, 没有问题。

但是, 当我们在 App 组件中向 Home 组件传入 props.children 时, 就会出现问题(此问题不仅限于我下面例子中传入了一个 About 组件, 传入任何元素都会出现这个问题, 即使我们传入一个简单的 div 元素):

import { useState } from "react";
import Home from "./pages/Home";
import About from "./pages/About";function App() {const [count, setCount] = useState(0);console.log("App is render");return (<div className="App">{count}<button onClick={() => setCount(count + 1)}>Increment</button><Home><About /></Home></div>);
}

About 组件同样使用 memo 包裹, 代码如下:

import React, { memo } from "react";const About = memo(() => {console.log("About is render");return <div>About</div>;
});export default About;

此时如果我们修改 count 的值, 会导致 App 组件重新渲染, 但是也会导致 Home 组件重新渲染。这就有些令人疑惑, 我们来分析一下:

首先我们知道, 在未经过任何优化的情况下, 父组件重新渲染一定会导致子组件的重新渲染, 那么也就会创建一个新的组件实例; 而如果使用 memo 对组件进行包裹, 那么在组件的 props 和自身状态没有发生变化的情况下, 父组件重新渲染子组件不会重新渲染, 是不是意味着不会创建一个新的组件实例呢? (这里进入了思维误区)

上面代码中, 我们向 Home 组件中传递了一个 About 组件, 目前 Home 组件中的表现就相当于 props.children = <About/>, 由于 Home 组件被 memo 包裹还重新渲染了, 那大几率是 props 发生了变化。纠结之处就在于, 此时 props 中又只有 children 一个属性, 值为 About 组件, About 组件同样被 memo 包裹, 且没有依赖任何 props 和状态, 如果 About 组件返回的结果应该是相同的, 就不应该导致 Home 组件的 props 发生变化才对。

这就是我所遇到的问题, 为什么 props.children 会影响组件的渲染呢?

问题分析

我依然怀疑是由 Home 组件的 props 发生了变化, 唯一可能变化的就是 About 组件, 为了验证我的想法, 于是我在Home 组件中定义了一个 aboutRef 变量, 使用 useRef 包裹 About 组件, 如下所示:

import Home from "./pages/Home";
import { useState } from "react";function App() {const [count, setCount] = useState(0);// 使用useRef包裹const aboutRef = useRef(<About/>);console.log("App is render");return (<div className="App">{count}<button onClick={() => setCount(count + 1)}>Increment</button><Home>{aboutRef.current}</Home></div>);
}

此时我发现, 首次渲染时 App、Home、About 都会渲染, 而当 count 发生变化时, 只有 App 组件重新渲染了, 这也就达到了我最初期望的效果。但是为什么包裹了 useRef 才可以做到这个效果呢? 到这里已经可以确定的是 Home 组件的 props.children 一定是发生了变化的, 那么我们来探讨一下 About 组件为什么会变化。

变化的原因是因为组件每次重新渲染时都会创建 React 元素, 例如<About /> = jsx(About), 并且在调用时会返回一个新对象, 当然不只是 About 会这样创建, 其他组件和元素也是这样创建的。其中jsx()只不过是React.createElement 的语法糖而已, 元素或组件都会通过 React.createElement 创建返回一个 ReactElement 对象, 这是因为 React 利用 ReactElement 对象组成了一个 Javascript 对象树(也就是虚拟 DOM )。前面我进入了一个思维误区, 认为 memo 包裹的组件不会再被重新创建了, 其实不管是否有memo包裹, 都是会通过 React.createElement 来创建, 只不过被memo包裹的组件创建出来的 React 元素会有所不同, 具体的可以深入的学习 memo, 这里给大家推荐一篇文章《从源码学 API 系列之 React.memo》。

因此对于 props.children 而言, 每次得到的都是 React.createElement(About) 返回的一个新对象, 这也是 Home 组件的 props 改变了的原因; 而我们使用 useRef, 创建了一个不会改变的对象赋值给 Home 组件的 props, 所以 Home 组件的 props 没有发生变化, 就不会重新渲染。

解决方案

解决这个问题, 除了使用 useRef 之外, 我们还可以定义一个变量, 提到 App 组件外, 也可以做到这个效果, 如下所示:

import { useState } from "react";
import Home from "./pages/Home";
import About from "./pages/About";// 在组件外定义变量
const about = <About />;function App() {const [count, setCount] = useState(0);console.log("App is render");return (<div className="App">{count}<button onClick={() => setCount(count + 1)}>Increment</button><Home>{about}</Home></div>);
}

当 About 组件没有依赖于 App 组件中其他状态时, 我们可以采用上面的做法, 但是如果 About 组件还依赖 App 内的其他状态, 可以发现无论是提变量还是 useRef 的做法都无法实现, 例如 About 组件中接收一个 name 参数, 由 App 组件传入:

import React, { memo } from "react";// 接收一个props.name
const About = memo(({ name }) => {console.log("About is render");return <div>About: {name}</div>;
});export default About;

这个时候我们就需要借助于 useMemo 进行优化(不用 useCallback 的原因是 useCallback 作用于函数, useMemo 作用于返回值, 在这里很明显我们想要作用于函数返回的组件), 就做到了实现当 count 发生变化时, 只有 App 组件重新渲染, 而 name 属性变化时 App、Home、About 都会重新渲染:

function App() {const [count, setCount] = useState(0);// 传入About组件的状态const [name, setName] = useState("Hello");// 使用useMemo优化const about = useMemo(() => <About name={name} />, [name]);console.log("App is render");return (<div className="App">{count}<button onClick={() => setCount(count + 1)}>Increment</button><button onClick={() => setName("abc")}>Change Name</button><Home>{about}</Home></div>);
}

相关文章:

React中每次渲染都会传入一个新的props.children到子组件?

传入props.children后, 为什么会导致组件的重新渲染&#xff1f; 问题描述 在 react 中, 我想要对组件的渲染进行优化, 遇到了一个非常意思的问题, 当我向一个组件中传入了 props.children 之后, 每次父组件重新渲染都会导致这个组件的重新渲染; 它看起来的表现就像是被memo包…...

Qt 通过命令行编译程序

前言 从服务器拉代码到编译成可执行文件一个脚本解决问题。使用的项目文件见上一个文章 Qt生成动态链接库并使用动态链接库 脚本代码 为了方便易懂这是一个很简单的Qt编译脚本 call E:\vs2015\VC\vcvarsall.bat x86 rmdir /s /q my-project git clone gitgitee.com:wenbai1…...

WireShark监控浏览器登录过程网络请求

软件开发中经常前后端扯皮。一种是用Chrome浏览器的开发者工具 来看网络交互&#xff0c;但是前提是 网络端口的确是通的。 WireShark工作在更低层。 这个工具最大的好处&#xff0c;大家别扯皮&#xff0c;看网络底层的log&#xff0c;到底 你的端口开没开&#xff0c; 数据…...

202301209将RK3399的挖掘机开发板在Android10下设置系统默认为24小时制

202301209将RK3399的挖掘机开发板在Android10下设置系统默认为24小时制 2023/12/9 22:07 应该也可以适用于RK3399的Android12系统 --- a/frameworks/base/packages/SettingsProvider/res/values/defaults.xml b/frameworks/base/packages/SettingsProvider/res/values/default…...

智能优化算法应用:基于法医调查算法无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于法医调查算法无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于法医调查算法无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.法医调查算法4.实验参数设定5.算法结果6.参考…...

使用MfgTool烧写工具烧写自制系统

一. 简介 本文我们就来学习&#xff0c;如何将我们编译的 uboot&#xff0c;zImage&#xff08;内核镜像&#xff09;&#xff0c;xxx.dtb设备树文件&#xff0c;还有制作的根文件系统&#xff0c;这四个文件烧写到开发板中&#xff0c;最后 开发板能正常启动。 上一篇文章说…...

react中使用react-konva实现画板框选内容

文章目录 一、前言1.1、API文档1.2、Github仓库 二、图形2.1、拖拽draggable2.2、图片Image2.3、变形Transformer 三、实现3.1、依赖3.2、源码3.2.1、KonvaContainer组件3.2.2、use-key-press文件 3.3、效果图 四、最后 一、前言 本文用到的react-konva是基于react封装的图形绘…...

es6 相关面试总结

1、es6 是什么 新一代的js 语言标准&#xff0c;对其核心做了升级优化&#xff0c;更加适合大型应用开发。 2、箭头函数优缺点 优点&#xff1a; 1.代码优化 2.this 指向不会变动&#xff0c;永远指向其父元素 缺点&#xff1a; 1.没有arguments 参数 2.不能通过 appl…...

【Hive】——数据仓库

1.1 数仓概念 数据仓库&#xff08;data warehouse&#xff09;&#xff1a;是一个用于存储&#xff0c;分析&#xff0c;报告的数据系统 目的&#xff1a;是构建面向分析的集成化数据环境&#xff0c;分析结果为企业提供决策支持 特点&#xff1a; 数据仓库本身不产生任何数据…...

算法基础九

螺旋矩阵2 给你一个正整数 n &#xff0c;生成一个包含 1 到 n2 所有元素&#xff0c;且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix。 示例 1&#xff1a; 输入&#xff1a;n 3 输出&#xff1a;[[1,2,3],[8,9,4],[7,6,5]] 示例 2&#xff1a; 输入&#xff1a;n …...

QT-在ui界面中给QWidget增加Layout布局的两种方法

2023-12-05 QT-在ui界面中给QWidget增加Layout布局的两种方法 方式一 在UI界面&#xff0c;用拖拽的方式加入Layout方式二 用notepad软件打开.ui文件&#xff0c;手动加入Layout代码 目标&#xff1a;去除右下角红标&#xff0c;给tab标签增加Layout属性。 方式一 在UI界面&am…...

免费的网页数据抓取工具有哪些?【2024附下载链接】

在网络上&#xff0c;有许多网页数据抓取工具可供选择。本文将探讨其如何全网采集数据并支持指定网站抓取。我们将比较不同的数据采集工具&#xff0c;帮助您找到最适合您需求的工具。 网页数据抓取工具种类 在选择网页数据抓取工具之前&#xff0c;让我们先了解一下这些工具…...

报错:Parsed mapper file: ‘file mapper.xml 导致无法启动

报错 &#xff1a; Logging initialized using class org.apache.ibatis.logging.stdout.StdOutImpl adapter. Registered plugin: com.github.yulichang.interceptor.MPJInterceptor3b2c8bda Parsed mapper file: file [/Mapper.xml] application无法启动 我这边产生原因是项…...

Linux驱动开发学习笔记2《LED驱动开发试验》

目录 一、Linux下LED灯驱动原理 1.地址映射 二、硬件原理图分析 三、实验程序编写 1.LED 灯驱动程序编写 2.编写测试APP 四、运行测试 1.编译驱动程序和测试APP &#xff08;1&#xff09;编译驱动程序 &#xff08;2&#xff09;编译测试APP 2.运行测试 一、Linux下…...

hive数据库查看参数/hive查看当前环境配置

文章目录 一、hive查看当前环境配置命令 在一次hive数据库执行命令 set ngmr.exec.modecluster时&#xff0c;想看一下 ngmr.exec.mode参数原先的值是什么&#xff0c;所以写一下本篇博文&#xff0c;讲一下怎么查看hive中的参数。 一、hive查看当前环境配置命令 set &#…...

ajax中get和post的区别,datatype返回的数据类型有哪些?web开发中数据提交的几种方式,有什么区别。百度使用哪种方式?

在Ajax中&#xff0c;GET和POST是两种常见的HTTP请求方法。它们有以下区别&#xff1a; GET请求&#xff1a;使用GET请求时&#xff0c;参数数据会附加在URL的末尾&#xff0c;以查询字符串的形式发送给服务器。GET请求是幂等的&#xff0c;也就是说多次发送相同的GET请求&…...

STM32用flash保存参数实现平衡擦写的一种方法

#FLASH平衡擦写# 一、概述 简易示意图如下&#xff1a; 写参数前要擦除对应的扇区 全为0XFFFFFFFF操作的最小单位为32位 uint32_t; 当一块扇区写完时&#xff0c;将所有有用参数复制到第二块扇区&#xff0c;开始写新的参数&#xff0c;如果所有参数写完&#xff0c;又重第…...

Aho Corasick Algorithm

文章目录 前言介绍实现参考 前言 Aho Corasick Algorithm又叫AC自动机&#xff0c;该算法是一个匹配算法&#xff0c;用来匹配文本Text中多个patterns分别出现的次数&#xff1b; 我们定义n为patterns的总长度&#xff1b;m为Text的长度&#xff1b; 问题&#xff1a;在ahis…...

用户管理 --汇总

一、第一节课 1.1 本人写的 前端&#xff1a; 鱼皮 --&#xff1e; 用户中心 第1节课-CSDN博客 中期&#xff1a; 一、用户管理 第1节课中间-CSDN博客 后端&#xff1a; 一、用户管理-CSDN博客 其他的链接 亿图脑图MindMaster 1.2 优秀球友&#xff0c;推荐 Docs 另…...

Flutter视频播放器在iOS端和Android端都能实现全屏播放

Flutter开发过程中&#xff0c;对于视频播放的三方组件有很多&#xff0c;在Android端适配都挺好&#xff0c;但是在适配iPhone手机的时候&#xff0c;如果设置了UIInterfaceOrientationLandscapeLeft和UIInterfaceOrientationLandscapeRight都为false的情况下&#xff0c;无法…...

洛雪音乐音源终极配置指南:三步解决音乐播放难题

洛雪音乐音源终极配置指南&#xff1a;三步解决音乐播放难题 【免费下载链接】lxmusic- lxmusic(洛雪音乐)全网最新最全音源 项目地址: https://gitcode.com/gh_mirrors/lx/lxmusic- 你是否经常遇到音乐播放器找不到想听的歌曲&#xff1f;是否厌倦了在各个平台间切换只…...

迷拟极速飞车——极致竞速新体验,重塑线下轻娱新标杆

随着国内文旅休闲、商业游乐行业的快速发展&#xff0c;消费者的线下娱乐审美与体验标准持续升级。传统游乐项目模式固化、玩法单一&#xff0c;同质化问题愈发突出&#xff0c;千篇一律的休闲设施早已无法满足全年龄段游客的多元化游玩需求。无论是城市商业综合体、城郊文旅景…...

Fortune.js未来路线图:即将推出的功能和改进终极指南

Fortune.js未来路线图&#xff1a;即将推出的功能和改进终极指南 【免费下载链接】fortune Non-native graph database abstraction layer for Node.js and web browsers. 项目地址: https://gitcode.com/gh_mirrors/fo/fortune Fortune.js作为一个创新的非原生图数据库…...

鸣潮自动化终极指南:解放双手,轻松享受游戏乐趣的完整解决方案

鸣潮自动化终极指南&#xff1a;解放双手&#xff0c;轻松享受游戏乐趣的完整解决方案 【免费下载链接】ok-wuthering-waves 鸣潮 后台自动战斗 自动刷声骸 一键日常 Automation for Wuthering Waves 项目地址: https://gitcode.com/GitHub_Trending/ok/ok-wuthering-waves …...

7个革命性策略:戴森球计划工厂蓝图全生命周期管理指南

7个革命性策略&#xff1a;戴森球计划工厂蓝图全生命周期管理指南 【免费下载链接】FactoryBluePrints 游戏戴森球计划的**工厂**蓝图仓库 项目地址: https://gitcode.com/GitHub_Trending/fa/FactoryBluePrints 想要在戴森球计划中建立高效工厂却总是遭遇物流瓶颈&…...

ComfyUI-Impact-Pack:AI图像精细化增强的3大突破性技术革命

ComfyUI-Impact-Pack&#xff1a;AI图像精细化增强的3大突破性技术革命 【免费下载链接】ComfyUI-Impact-Pack Custom nodes pack for ComfyUI This custom node helps to conveniently enhance images through Detector, Detailer, Upscaler, Pipe, and more. 项目地址: htt…...

8通道采集控制终端:工业物联网边缘智能的核心硬件解析

1. 项目概述&#xff1a;从“通道”到“终端”的工业物联进化最近在调试一个老旧产线的数据采集项目&#xff0c;现场一堆4-20mA的传感器、干接点的报警信号&#xff0c;还有几个需要远程启停的电机&#xff0c;线缆接得跟蜘蛛网一样。甲方负责人看着头疼&#xff0c;问我有没有…...

智能网络资源下载器:轻松捕获微信、抖音、小红书等平台内容

智能网络资源下载器&#xff1a;轻松捕获微信、抖音、小红书等平台内容 【免费下载链接】res-downloader 视频号、小程序、抖音、快手、小红书、直播流、m3u8、酷狗、QQ音乐等常见网络资源下载! 项目地址: https://gitcode.com/GitHub_Trending/re/res-downloader 你是否…...

限流算法详解 - 滑动窗口算法深入理解

滑动窗口算法详解 专门针对滑动窗口算法&#xff0c;从原理到精确限流的实现细节做一个深入剖析。 一、核心思想 固定窗口在时间边界处会出现“计数突跳”&#xff0c;原因是窗口的边界是硬重置的&#xff08;0→1秒末清空&#xff0c;1→2秒初重新计数&#xff09;。 滑动窗口…...

ESP32音频录制系统:构建智能声音采集的完整解决方案

ESP32音频录制系统&#xff1a;构建智能声音采集的完整解决方案 【免费下载链接】esp32_SoundRecorder ESP32 Sound recorder with simple code in arduino-esp32. (I2S interface) 项目地址: https://gitcode.com/gh_mirrors/es/esp32_SoundRecorder 在物联网和嵌入式系…...