当前位置: 首页 > 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;无法…...

多模态2025:技术路线“神仙打架”,视频生成冲上云霄

文&#xff5c;魏琳华 编&#xff5c;王一粟 一场大会&#xff0c;聚集了中国多模态大模型的“半壁江山”。 智源大会2025为期两天的论坛中&#xff0c;汇集了学界、创业公司和大厂等三方的热门选手&#xff0c;关于多模态的集中讨论达到了前所未有的热度。其中&#xff0c;…...

Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)

文章目录 1.什么是Redis&#xff1f;2.为什么要使用redis作为mysql的缓存&#xff1f;3.什么是缓存雪崩、缓存穿透、缓存击穿&#xff1f;3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...

【HarmonyOS 5.0】DevEco Testing:鸿蒙应用质量保障的终极武器

——全方位测试解决方案与代码实战 一、工具定位与核心能力 DevEco Testing是HarmonyOS官方推出的​​一体化测试平台​​&#xff0c;覆盖应用全生命周期测试需求&#xff0c;主要提供五大核心能力&#xff1a; ​​测试类型​​​​检测目标​​​​关键指标​​功能体验基…...

mongodb源码分析session执行handleRequest命令find过程

mongo/transport/service_state_machine.cpp已经分析startSession创建ASIOSession过程&#xff0c;并且验证connection是否超过限制ASIOSession和connection是循环接受客户端命令&#xff0c;把数据流转换成Message&#xff0c;状态转变流程是&#xff1a;State::Created 》 St…...

rnn判断string中第一次出现a的下标

# coding:utf8 import torch import torch.nn as nn import numpy as np import random import json""" 基于pytorch的网络编写 实现一个RNN网络完成多分类任务 判断字符 a 第一次出现在字符串中的位置 """class TorchModel(nn.Module):def __in…...

Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信

文章目录 Linux C语言网络编程详细入门教程&#xff1a;如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket&#xff08;服务端和客户端都要&#xff09;2. 绑定本地地址和端口&#x…...

站群服务器的应用场景都有哪些?

站群服务器主要是为了多个网站的托管和管理所设计的&#xff0c;可以通过集中管理和高效资源的分配&#xff0c;来支持多个独立的网站同时运行&#xff0c;让每一个网站都可以分配到独立的IP地址&#xff0c;避免出现IP关联的风险&#xff0c;用户还可以通过控制面板进行管理功…...

「全栈技术解析」推客小程序系统开发:从架构设计到裂变增长的完整解决方案

在移动互联网营销竞争白热化的当下&#xff0c;推客小程序系统凭借其裂变传播、精准营销等特性&#xff0c;成为企业抢占市场的利器。本文将深度解析推客小程序系统开发的核心技术与实现路径&#xff0c;助力开发者打造具有市场竞争力的营销工具。​ 一、系统核心功能架构&…...

WEB3全栈开发——面试专业技能点P7前端与链上集成

一、Next.js技术栈 ✅ 概念介绍 Next.js 是一个基于 React 的 服务端渲染&#xff08;SSR&#xff09;与静态网站生成&#xff08;SSG&#xff09; 框架&#xff0c;由 Vercel 开发。它简化了构建生产级 React 应用的过程&#xff0c;并内置了很多特性&#xff1a; ✅ 文件系…...

针对药品仓库的效期管理问题,如何利用WMS系统“破局”

案例&#xff1a; 某医药分销企业&#xff0c;主要经营各类药品的批发与零售。由于药品的特殊性&#xff0c;效期管理至关重要&#xff0c;但该企业一直面临效期问题的困扰。在未使用WMS系统之前&#xff0c;其药品入库、存储、出库等环节的效期管理主要依赖人工记录与检查。库…...