React 重新渲染深度解析:为何 children 组件会被重复渲染及优化策略


react 重新渲染深度解析:为何 children 组件会被重复渲染及优化策略

本文深入探讨了 React 组件在父组件状态更新时,即使通过 children prop 传递,子组件仍可能被重复渲染的常见问题。核心原因在于父组件每次渲染时,若子组件在 JSX 中被内联声明,React 会创建新的子组件实例。文章通过具体代码示例,详细解释了这一机制,并提供了将状态管理下移以稳定 children prop 的优化方案,旨在帮助开发者更好地理解和优化 React 应用的渲染性能。

理解 React 组件的重新渲染机制

在 React 应用开发中,组件的重新渲染(re-render)是一个核心概念,但其行为有时会超出开发者的预期。一个常见的误解是,如果一个父组件的 children prop 逻辑上没有改变,那么传递给它的子组件就不会重新渲染。然而,当父组件自身因状态更新而重新渲染时,如果子组件是在父组件的 JSX 渲染逻辑中内联声明的,即使其内容看似不变,React 也会将其视为一个新的组件实例,从而触发子组件的重新渲染。

这背后的机制是:每当一个组件函数执行时(即发生渲染),如果其 JSX 中包含对另一个组件的引用,React 会为该引用创建一个新的 React 元素(React Element)。即使这个新的 React 元素在类型和 props 上与上一次渲染的元素完全相同,但由于它是一个“新”的对象实例,React 在协调(reconciliation)过程中会认为这个子组件可能需要更新,并会访问其子树进行比对,最终导致子组件函数被再次调用,即重新渲染。

问题示例:children 组件的意外重新渲染

考虑以下 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 表达式 都会被重新评估。这意味着,每次 App 渲染时,都会创建一个新的 React 元素实例。

尽管这个新的 元素与上一次渲染的 元素在类型和结构上是相同的,但对 React 而言,它是一个全新的 J*aScript 对象引用。因此,当 App 将这个新的 元素作为 Parent 的 children prop 传递时,React 会认为 Parent 组件的 children prop 已经改变(因为它是一个新的对象引用),从而触发 Parent 和其 children(即 Child 组件)的重新渲染。

优化方案:稳定化 children Prop

为了避免 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 组件在首次渲染后,其内部将不再有状态更新导致自身重新渲染。这意味着 这部分 JSX 表达式只会在 App 组件挂载时执行一次。因此,传递给 Parent 的 React 元素引用将保持稳定。

现在,Parent 组件会每 100 毫秒更新其内部的 now 状态并重新渲染。然而,由于 App 组件不再重新渲染,它传递给 Parent 的 children prop(即 元素)的引用始终是第一次创建的那个。当 Parent 重新渲染时,React 发现其 children prop 的引用没有改变,因此不会访问 Child 组件的子树,Child 组件也就不会重新渲染。此时,控制台将只输出一次 Child rendered。

注意事项与最佳实践

  1. 状态下移原则: 尽可能将状态和副作用放置在组件树中需要它们的最底层组件。这可以有效限制重新渲染的范围,避免不必要的性能开销。

  2. 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 每次都传递一个新的 元素给 Parent 的 children prop,那么 Parent 的 children prop 引用仍然会改变,Parent 依然会重新渲染,并进而渲染其 children。因此,React.memo 主要用于优化组件自身因父组件 props 变化而导致的重新渲染,而不是解决父组件内联创建子组件实例的问题。

  3. 理解 JSX 的本质: JSX 语法糖最终会被编译成 React.createElement() 调用。每次父组件渲染时,JSX 中的组件标签都会被转换为新的 React.createElement() 调用,生成新的 React 元素对象。理解这一点有助于避免关于组件重新渲染的误解。

  4. 组件组合的权衡: “提升内容” (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


相关推荐: 163邮箱网页版入口 163邮箱在线使用  《百果园》充值余额方法  米侠浏览器插件无法启用怎么办 米侠浏览器扩展兼容性修复  Yandex无需登录畅游 俄罗斯搜索引擎最新官网指南  Win10关闭UAC用户账户控制的方法 Win10降低安全提示等级【技巧】  《长生:天机降世》火塔小怪大全  HTML中多图片上传与预览:解决ID冲突的专业指南  漫蛙漫画官方版直通入口 2025漫蛙漫画免注册访问说明  Win10通知横幅停留时间修改 Win10自定义通知显示时长【技巧】  windows10怎么开启wsl_windows10安装linux子系统教程  composer licenses 命令:如何检查项目依赖的许可证?  Flexbox布局实践:实现底部页脚与顶部粘性导航条的完美结合  Win10如何彻底关闭OneDrive Win10禁用云同步功能【纯净】  《图怪兽》退出登录方法  电脑的“恢复环境(WinRE)”找不到怎么办_Windows系统恢复环境重建【高级修复】  我的世界官方网址入口 我的世界游戏主页直达入口  《爱笔思画x》涂色教程  天天漫画2025最新入口 天天漫画永久有效登录入口  百度地图离线地图无法加载如何解决 百度地图离线地图加载优化方法  原子笔记app误删找回教程  猫眼app抢票快还是小程序快  怎样设置开机后自动运行某个程序_Windows启动文件夹与任务计划【自动化】  手机耗电快是什么原因 延长手机电池续航时间的设置方法【详解】  Google Drive API服务器端访问指南:服务账户认证详解  鸿蒙单条备忘录如何加密  使用document.execCommand实现Web文本编辑器加粗/取消加粗  HTML Canvas文本样式定制指南:解决外部字体加载与应用难题  VS Code的时间线(Timeline)视图:您的代码时光机  《友玩*》创建群聊方法  知音漫客官网首页入口_知音漫客热门漫画推荐  Go Goroutine调度与并发执行深度解析  暴风影音官网正式版_暴风影音手机版官网下载安卓  如何在mysql中使用索引提示_mysql索引提示优化方法  126邮箱申请入口官网_126邮箱注册免费登录2025  解决jQuery多计算器输入字段冲突的教程  《异星探险家》古怪的物品作用介绍  Win11便笺在哪打开 Win11桌面便笺(Sticky Notes)使用方法【详解】  Dagster资产间数据传递与用户配置管理教程  Mac如何开启画中画模式_Mac Safari浏览器视频画中画功能  PPT智能排版生成入口 免费PPT内容自动生成平台  TikTok收藏夹无法删除视频如何解决 TikTok收藏管理优化方法  百度识图图像分析 百度识图识别平台  夸克浏览器资源嗅探怎么用 夸克浏览器网页资源下载技巧【教程】  Git命令与VS Code UI操作的对应关系解析  追剧达人如何发弹幕  QQ网站入口直接登录 QQ官方正版登录页面  阿里云共享相册入口在哪  微信步数怎么刷_微信步数快速提升技巧  京东快递物流信息不更新怎么办_物流停滞原因与处理方法  Python实时数据流中高效查找最大最小值 

 2025-10-06

了解您产品搜索量及市场趋势,制定营销计划

同行竞争及网站分析保障您的广告效果

点击免费数据支持

提交您的需求,1小时内享受我们的专业解答。

运城市盐湖区信雨科技有限公司


运城市盐湖区信雨科技有限公司

运城市盐湖区信雨科技有限公司是一家深耕海外推广领域十年的专业服务商,作为谷歌推广与Facebook广告全球合作伙伴,聚焦外贸企业出海痛点,以数字化营销为核心,提供一站式海外营销解决方案。公司凭借十年行业沉淀与平台官方资源加持,打破传统外贸获客壁垒,助力企业高效开拓全球市场,成为中小企业出海的可靠合作伙伴。

 8156699

 13765294890

 8156699@qq.com

Notice

We and selected third parties use cookies or similar technologies for technical purposes and, with your consent, for other purposes as specified in the cookie policy.
You can consent to the use of such technologies by closing this notice, by interacting with any link or button outside of this notice or by continuing to browse otherwise.