React.memo优化列表渲染方法
你在学习文章相关的知识吗?本文《React.memo优化列表渲染技巧》,主要介绍的内容就涉及到,如果你想提升自己的开发能力,就不要错过这篇文章,大家要知道编程理论基础和实战操作都是不可或缺的哦!
理解列表组件的重绘问题
在React中,当父组件的状态发生变化时,它会触发自身的重新渲染,进而导致其所有子组件也默认重新渲染。对于渲染列表的场景,如果列表数据通过useState管理,并且我们在列表中添加或移除了一个元素,即使列表中大部分现有元素的实际内容并未改变,它们也可能因为父组件的重绘而跟着重绘。这会带来不必要的性能开销,尤其当列表项复杂或数量庞大时,用户体验会受到影响。
例如,考虑以下场景:一个包含多个卡片组件的列表。当用户点击按钮添加一张新卡片时,我们期望只有新卡片被渲染,而现有卡片保持不变。然而,由于useState触发了父组件的重新渲染,导致所有卡片组件都重新执行其渲染逻辑,即使它们的props没有变化。这可以通过在子组件内部添加console.log来验证,你会发现即使未改变的卡片组件也会打印“Rendering Card”信息。
React的协调机制与key的重要性
在深入解决方案之前,理解React如何处理列表渲染至关重要。React使用一种称为“协调”(Reconciliation)的算法来高效更新UI。当状态或props发生变化时,React会构建一个新的虚拟DOM树,并与旧的虚拟DOM树进行比较,以确定实际需要更新到真实DOM的部分。
对于列表,React依赖于key属性来识别列表中每个元素的唯一性。一个稳定且唯一的key能够帮助React:
- 识别组件身份:精确追踪哪个元素被添加、移除或重新排序。
- 优化更新:如果一个元素的key没有改变,React会尝试复用现有的DOM元素,只更新其props。如果key改变了,React会销毁旧组件并创建新组件。
重要提示:在动态列表中,应避免使用数组索引作为key。因为当列表项的顺序发生变化、或者有元素被添加/删除时,索引会随之改变,导致React无法正确识别组件,可能引发性能问题或难以发现的bug。理想的key应是数据源中稳定且唯一的ID(例如数据库ID)。
解决方案:利用React.memo优化子组件渲染
为了阻止未改变的子组件进行不必要的重绘,我们可以使用React.memo。React.memo是一个高阶组件(Higher-Order Component),它会包裹一个函数式组件。当该组件的props发生变化时,React.memo会对其props进行浅层比较。如果props没有发生变化,React.memo就会阻止该组件的重新渲染,直接复用上一次的渲染结果。
如何应用React.memo
只需将需要优化的函数式组件用React.memo包裹起来即可。
示例代码:
import "./styles.css"; import React, { useState, memo } from "react"; // 引入 memo // 模拟数据 const fakeData1 = { Card1: [1, 2, 3, 4] }; const fakeData2 = { Card2: [5, 6, 7, 8] }; // 使用 React.memo 包装 Card 组件 // 只有当 Card 组件的 props (id, item, setCardArray) 发生变化时,它才会重新渲染 const Card = memo(({ id, item, setCardArray }) => { // 这里的 console.log 只会在 Card 组件实际渲染或 props 变化时打印 console.log("Rendering Card: ", item); const handleRemove = (event) => { if (event.type === "click" || event.type === "keydown") { setCardArray((entityState) => { const updatedData = { ...entityState }; // 注意:这里的删除逻辑是硬编码删除 fakeData2 // 在实际应用中,你需要根据传入的 id 或其他唯一标识符来删除对应的卡片 delete updatedData["fakeData2"]; return updatedData; }); } }; return ( <div style={{ border: "black solid 2px", padding: "50px 0", marginBottom: "10px" }}> <h1>Card - {id}</h1> <div>内容: {Object.values(item)}</div> <button onClick={handleRemove}>移除</button> </div> ); }); // 应用主组件 const fakeObject = { fakeData1 }; // 初始数据 export default function App() { const [cardArray, setCardArray] = useState(fakeObject); const addCard = () => { // 通过展开现有状态并添加新数据,确保状态更新的不可变性 setCardArray((entityState) => ({ ...entityState, fakeData2 // 硬编码添加 fakeData2 })); }; return ( <div className="App"> <button onClick={addCard}>添加一张卡片</button> <div style={{ marginTop: "20px" }}> {Object.values(cardArray) .flat() .map((item, index) => { // 尽管示例中使用了 index 作为 key 和 id, // 但在实际动态列表中,强烈建议使用数据中稳定且唯一的ID作为 key, // 以确保 React 能正确识别和优化组件。 return <Card setCardArray={setCardArray} id={index} key={index} item={item} />; })} </div> </div> ); }
在上述代码中,我们将Card组件用memo包裹起来。现在,当你点击“添加一张卡片”按钮时,App组件会重新渲染,cardArray状态会更新。但由于Card组件被memo包裹,React在渲染每个Card实例之前会检查其props。对于fakeData1对应的卡片,它的id和item(以及setCardArray,因为useState返回的setter函数是稳定的)都没有改变,因此React.memo会阻止其重绘,你将不会看到“Rendering Card: Card1”再次打印。只有新添加的Card2会触发渲染。
注意事项与最佳实践
React.memo并非万能:
- React.memo会进行props的浅层比较,这本身也有一定的开销。如果组件的props经常变化,或者组件本身的渲染成本很低,使用memo可能带来的性能提升微乎其微,甚至可能因为比较开销而略微降低性能。
- 只有在组件渲染成本较高、且其父组件频繁重绘但自身props不常变化时,React.memo才能发挥最大作用。
函数props与useCallback:
- 如果memo包裹的组件接收一个函数作为prop,并且这个函数在父组件每次渲染时都会被重新创建(例如,父组件内部定义的一个普通函数),那么即使函数逻辑相同,React.memo也会因为函数引用不同而认为prop发生了变化,导致子组件重新渲染。
- 在这种情况下,你需要使用useCallback Hook来缓存这个函数,确保它在依赖项不变的情况下,每次渲染都返回同一个函数引用。例如:
const handleClick = useCallback(() => { // ... }, [dependency1, dependency2]); // 然后将 handleClick 作为 prop 传递给 memo-ized 子组件
- 值得注意的是,useState返回的setter函数(如setCardArray)是稳定的,它们不会在每次渲染时重新创建,因此直接传递它们给memo包裹的组件是安全的,不需要useCallback。
状态更新的不可变性:
- 始终以不可变的方式更新状态。这意味着当你更新一个数组或对象时,应该创建一个新的数组或对象,而不是直接修改旧的。
- 在示例中,setCardArray((entityState) => ({ ...entityState, fakeData2 }))就是遵循不可变性的良好实践,它创建了一个新的对象来更新状态。
useRef的局限性:
- 问题中提到尝试过useRef。useRef主要用于在组件的整个生命周期内持有可变值,并且它的更新不会触发组件的重新渲染。因此,虽然useRef可以存储数据,但它无法像useState那样自动触发UI更新以显示新添加的元素,这也是为什么它不适用于此场景的主要原因。
总结
通过恰当地使用React.memo,结合对key属性的正确管理和状态不可变性原则,我们可以显著优化React列表组件的渲染性能,避免不必要的重绘。这不仅能提升应用的响应速度,还能为用户带来更流畅、更高效的交互体验。在开发React应用时,性能优化是一个持续的过程,理解并运用这些核心概念将帮助你构建更加健壮和高效的组件。
以上就是本文的全部内容了,是否有顺利帮助你解决问题?若是能给你带来学习上的帮助,请大家多多支持golang学习网!更多关于文章的相关知识,也可关注golang学习网公众号。

- 上一篇
- Golangmap优化技巧与避坑指南

- 下一篇
- HTML全屏背景设置方法
-
- 文章 · 前端 | 9分钟前 |
- JavaScript工厂模式详解
- 318浏览 收藏
-
- 文章 · 前端 | 10分钟前 |
- HTML5Module与Nomodule详解及应用
- 152浏览 收藏
-
- 文章 · 前端 | 15分钟前 |
- HTML类选择器使用方法及class属性详解
- 143浏览 收藏
-
- 文章 · 前端 | 16分钟前 |
- 浏览器渲染与事件循环顺序解析
- 413浏览 收藏
-
- 文章 · 前端 | 29分钟前 |
- CSS中box-shadow与text-shadow区别解析
- 346浏览 收藏
-
- 文章 · 前端 | 38分钟前 |
- 多任务并行执行技巧全解析
- 111浏览 收藏
-
- 文章 · 前端 | 39分钟前 |
- HTMLname属性详解与替代方案解析
- 157浏览 收藏
-
- 文章 · 前端 | 43分钟前 |
- HTML中display属性及8种显示方式详解
- 313浏览 收藏
-
- 文章 · 前端 | 44分钟前 |
- JavaScript数组去重技巧汇总
- 393浏览 收藏
-
- 文章 · 前端 | 45分钟前 |
- JS模式匹配原理与实战应用解析
- 438浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 542次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 511次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 498次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 484次学习
-
- 千音漫语
- 千音漫语,北京熠声科技倾力打造的智能声音创作助手,提供AI配音、音视频翻译、语音识别、声音克隆等强大功能,助力有声书制作、视频创作、教育培训等领域,官网:https://qianyin123.com
- 223次使用
-
- MiniWork
- MiniWork是一款智能高效的AI工具平台,专为提升工作与学习效率而设计。整合文本处理、图像生成、营销策划及运营管理等多元AI工具,提供精准智能解决方案,让复杂工作简单高效。
- 219次使用
-
- NoCode
- NoCode (nocode.cn)是领先的无代码开发平台,通过拖放、AI对话等简单操作,助您快速创建各类应用、网站与管理系统。无需编程知识,轻松实现个人生活、商业经营、企业管理多场景需求,大幅降低开发门槛,高效低成本。
- 218次使用
-
- 达医智影
- 达医智影,阿里巴巴达摩院医疗AI创新力作。全球率先利用平扫CT实现“一扫多筛”,仅一次CT扫描即可高效识别多种癌症、急症及慢病,为疾病早期发现提供智能、精准的AI影像早筛解决方案。
- 222次使用
-
- 智慧芽Eureka
- 智慧芽Eureka,专为技术创新打造的AI Agent平台。深度理解专利、研发、生物医药、材料、科创等复杂场景,通过专家级AI Agent精准执行任务,智能化工作流解放70%生产力,让您专注核心创新。
- 243次使用
-
- 优化用户界面体验的秘密武器:CSS开发项目经验大揭秘
- 2023-11-03 501浏览
-
- 使用微信小程序实现图片轮播特效
- 2023-11-21 501浏览
-
- 解析sessionStorage的存储能力与限制
- 2024-01-11 501浏览
-
- 探索冒泡活动对于团队合作的推动力
- 2024-01-13 501浏览
-
- UI设计中为何选择绝对定位的智慧之道
- 2024-02-03 501浏览