
在 React 应用开发中,组件的重新渲染(re-render)是一个核心概念,但其行为有时会超出开发者的预期。一个常见的误解是,如果一个父组件的 children prop 逻辑上没有改变,那么传递给它的子组件就不会重新渲染。然而,当父组件自身因状态更新而重新渲染时,如果子组件是在父组件的 JSX 渲染逻辑中内联声明的,即使其内容看似不变,React 也会将其视为一个新的组件实例,从而触发子组件的重新渲染。
这背后的机制是:每当一个组件函数执行时(即发生渲染),如果其 JSX 中包含对另一个组件的引用,React 会为该引用创建一个新的 React 元素(React Element)。即使这个新的 React 元素在类型和 props 上与上一次渲染的元素完全相同,但由于它是一个“新”的对象实例,React 在协调(reconciliation)过程中会认为这个子组件可能需要更新,并会访问其子树进行比对,最终导致子组件函数被再次调用,即重新渲染。
考虑以下 React 应用结构,其中 App 组件包含一个定时器,每 100 毫秒更新一次自身状态,进而导致 App 组件重新渲染。App 将 Child 组件作为 Parent 组件的 children prop 传递。
import { useState, useEffect } from 'react';
// Child 组件,每次渲染都会在控制台输出 'rendered'
const Child = () => {
console.log('Child rendered');
return (
<p>这是一个子组件内容。</p>
<div class="aritcle_card">
<a class="aritcle_card_img" href="/ai/1202">
<img src="https://img.php.cn/upload/ai_manual/001/431/639/68b7a1824cc48323.png" alt="CA.LA">
</a>
<div class="aritcle_card_info">
<a href="/ai/1202">CA.LA</a>
<p>第一款时尚产品在线设计平台,服装设计系统</p>
<div class="">
<img src="/static/images/card_xiazai.png" alt="CA.LA">
<span>86</span>
</div>
</div>
<a href="/ai/1202" class="aritcle_card_btn">
<span>查看详情</span>
<img src="/static/images/cardxiayige-3.png" alt="CA.LA">
</a>
</div>
);
}
// Parent 组件,接收并渲染 children prop
const Parent = ({ children }) => {
return (
<div id='parent'>
{children}
</div>
);
}
// App 组件,包含一个定时器更新自身状态
const App = () => {
const [now, setNow] = useState();
// 启动一个定时器,每100ms更新 'now' 状态
useEffect(() => {
const interval = setInterval(() =>
setNow(Date.now()), 100);
return () => clearInterval(interval); // 清理定时器
}, []);
return (
<div className="App">
<Parent>
{/* Child 组件在这里被内联声明 */}
<Child />
</Parent>
</div>
);
}
export default App;运行上述代码,你会发现控制台每 100 毫秒都会输出 Child rendered。尽管 Child 组件没有任何自身状态或接收任何 props,并且 Parent 组件也只是简单地渲染其 children prop,Child 依然在 App 组件状态更新时被重复渲染。
原因分析:
当 App 组件的状态 now 更新时,App 组件会重新执行其渲染函数。在每次执行时,App 组件的 JSX 表达式
尽管这个新的
为了避免 Child 组件在 App 组件状态更新时进行不必要的重新渲染,我们需要确保传递给 Parent 组件的 children prop 在 App 重新渲染时保持引用稳定。最直接有效的方法是将导致重新渲染的状态或副作用下移到组件树中更低层级的组件,使其不会影响到不相关的上层组件或兄弟组件。
在我们的例子中,App 组件中的定时器状态更新是导致 Child 重新渲染的根本原因。如果我们将这个定时器逻辑移动到 Parent 组件内部,那么 App 组件将不再因定时器而重新渲染,从而稳定了传递给 Parent 的 children prop。
import { useState, useEffect } from 'react';
const Child = () => {
console.log('Child rendered');
return (
<p>这是一个子组件内容。</p>
);
}
// Parent 组件,现在包含了定时器状态
const Parent = ({ children }) => {
const [now, setNow] = useState(); // 状态移至 Parent
// 启动一个定时器,每100ms更新 'now' 状态
useEffect(() => {
const interval = setInterval(() =>
setNow(Date.now()), 100);
return () => clearInterval(interval);
}, []);
return (
<div id='parent'>
{children}
</div>
);
}
// App 组件现在不包含定时器逻辑
const App = () => {
return (
<div className="App">
<Parent>
{/* Child 组件仍然在这里被内联声明 */}
<Child />
</Parent>
</div>
);
}
export default App;通过将 useState 和 useEffect 钩子从 App 组件移动到 Parent 组件,App 组件在首次渲染后,其内部将不再有状态更新导致自身重新渲染。这意味着
现在,Parent 组件会每 100 毫秒更新其内部的 now 状态并重新渲染。然而,由于 App 组件不再重新渲染,它传递给 Parent 的 children prop(即
状态下移原则: 尽可能将状态和副作用放置在组件树中需要它们的最底层组件。这可以有效限制重新渲染的范围,避免不必要的性能开销。
React.memo 的作用: 对于功能组件,可以使用 React.memo 来进行性能优化。React.memo 会对组件的 props 进行浅比较,如果 props 没有改变,则跳过组件的重新渲染。在上述示例中,即使 App 仍然包含定时器,如果 Child 组件被 React.memo 包裹,并且它不接收任何 props,它将不会重新渲染。但请注意,React.memo 仅在 props 引用稳定时才有效。如果像原始问题中那样,每次父组件渲染都创建一个新的 Child 元素,即使 Child 被 memo 包裹,它仍然会接收到一个“新”的 children prop 引用,从而导致重新渲染。
const MemoizedChild = React.memo(() => {
console.log('MemoizedChild rendered');
return <p>这是一个被 memoized 的子组件内容。</p>;
});
// ... 在 App 中使用 <MemoizedChild />然而,在我们的原始问题场景中,Child 是作为 Parent 的 children prop 传递的。如果 Parent 被 React.memo 包裹,而 App 每次都传递一个新的
理解 JSX 的本质: JSX 语法糖最终会被编译成 React.createElement() 调用。每次父组件渲染时,JSX 中的组件标签都会被转换为新的 React.createElement() 调用,生成新的 React 元素对象。理解这一点有助于避免关于组件重新渲染的误解。
组件组合的权衡: “提升内容” (lifting content up) 是一种强大的组合模式,可以将子组件的渲染逻辑与父组件的业务逻辑解耦。但在使用时,需要注意其对渲染性能的影响,确保传递的 children prop 引用在不必要时是稳定的。
React 的渲染机制是其高性能的基础,但如果不深入理解其工作原理,可能会遇到一些性能陷阱。当父组件重新渲染且在 JSX 中内联声明子组件时,React 会创建新的子组件实例,即使其逻辑内容不变,也可能导致不必要的重新渲染。通过将状态和副作用下移到更合适的组件层级,我们可以有效稳定传递给子组件的 children prop 引用,从而避免这些不必要的重新渲染,提升应用的性能和用户体验。
以上就是React 重新渲染深度解析:为何 children 组件会被重复渲染及优化策略的详细内容,更多请关注其它相关文章!
# javascript
# react
# 创建一个
# 是一个
# 使其
# 这是一个
# 子树
# red
# 组件渲染
# 常见问题
# 应用开发
# app
# js
# java
# 肇庆医疗网站建设推广
# seo推广的因素
# 揭阳seo按天收费
# 南京网站关键词优化报价
# 台商开发区行业网站推广
# 金华专业的seo网站推广价格
# 公众号营销软文推广方式
# 股票五大关键词排名查询
# seo优化佛山
# 德化县定制网站建设设计
# 移到
# 表单
# 它是
# 会在
# 在这里
相关栏目:
【
Google疑问12 】
【
Facebook疑问10 】
【
优化推广96088 】
【
技术知识133117 】
【
IDC资讯59369 】
【
网络运营7196 】
【
IT资讯61894 】
相关推荐:
《兴业银行》注册登录方法
铁路12306怎么申请退票_铁路12306退票申请操作流程
搜狗浏览器如何查找页面中的文字 搜狗浏览器Ctrl+F页面搜索功能
Go反射进阶:访问内嵌结构体中的被遮蔽方法
J*aScript大数运算_BigInt使用指南
《腾讯相册管家》注销账号方法
Python实战:高效处理实时数据流中的最小/最大值
快手缓存清理方法
Golang如何操作指针参数_Go pointer参数传递规则
Retrofit根路径POST请求:@POST("/") 的应用与解析
热血江湖归来医师加点攻略
TikTok视频播放中断怎么办 TikTok播放异常修复方法
我居然低估了 DeepSeek,这次更新它做到了这些!
拷贝漫画2025网页版入口 拷贝漫画官网免费看全集
c++如何链接Boost库_c++准标准库的集成与使用
百度网盘如何设置上传限额
解决 Vue 3 组件未定义错误:理解 createApp 与根组件的正确使用
铁路12306官网入口 铁路12306中国铁路官网登录首页
Fedora怎么安装 Fedora Workstation安装步骤
Win10如何关闭开机锁屏界面_Windows10跳过锁屏直接登录设置
Animex动漫社社登录官网 Animex动漫社资源社入口直达
优化Google Charts Gauge:在数据库无数据时显示默认值
百度浏览器无法安装扩展程序_百度浏览器插件安装失败原因解析
@Team是什么?揭秘团队含义
Python测试中模块导入路径解析的最佳实践
mysql中如何分析索引使用情况_mysql索引使用分析方法
Sublime Text怎么关闭自动完成_Sublime禁用Auto Complete设置
如何在CSS中实现盒模型多列间距_grid-gap与padding结合
Dash应用中自定义HTML页面标题与网站图标(F*icon)的实用指南
使用AI在VS Code中将代码从一种语言翻译成另一种
Win10怎么设置快速启动 Win10开启快速启动设置方法
《火影忍者:木叶高手》快速升级攻略
汽水音乐车机版官网5.0 汽水音乐车机版5.0版本下载入口
iQOO手机信号差网络不稳定怎么办 信号问题原因排查与增强设置【攻略】
iPhone 14 Pro如何更改区域设置_iPhone 14 Pro地区语言修改教程
J*aScript文本高亮功能优化:解决多词匹配错误与精确分割策略
在VS Code中进行数据科学和机器学习开发
Go Template中优雅处理循环最后一项:自定义函数实践
解决Flex容器横向滚动内容截断与偏移问题
CodeIgniter 3 中基于 MySQL 数据高效生成动态图表教程
j*a中赋值运算符是什么?
cad怎么隐藏指定的图层_cad隐藏或冻结图层方法
歌词怎么展示在|直播|间视频号?有什么注意事项?
Highcharts雷达图轴线交点数值标注指南
J*aScript中高效处理用户输入:从Keyup事件到表单提交的优化实践
lol小红书怎么|直播|?lol小红书|直播|是什么意思?
第五人格PC版怎么避免被封号_第五人格PC版防封号注意事项
Cassandra中复合主键、二级索引与ORDER BY排序的限制与解决方案
韩剧圈正版官网入口_韩剧圈官方指定登录
抖音网页版官方链接 抖音网页版官网链接入口
2025-10-06
运城市盐湖区信雨科技有限公司是一家深耕海外推广领域十年的专业服务商,作为谷歌推广与Facebook广告全球合作伙伴,聚焦外贸企业出海痛点,以数字化营销为核心,提供一站式海外营销解决方案。公司凭借十年行业沉淀与平台官方资源加持,打破传统外贸获客壁垒,助力企业高效开拓全球市场,成为中小企业出海的可靠合作伙伴。