React列表优化:避免元素重复渲染技巧
大家好,今天本人给大家带来文章《React列表渲染优化:防止数组元素重复渲染》,文中内容主要涉及到,如果你对文章方面的知识点感兴趣,那就请各位朋友继续看下去吧~希望能真正帮到你们,谢谢!
1. 理解React中的组件重渲染问题
在React应用开发中,当父组件的状态(State)发生变化时,默认情况下其所有子组件都会重新渲染。对于渲染列表(例如通过Array.prototype.map方法)的场景,如果列表数据存储在useState管理的数组中,那么当数组发生增删改操作时,即使某些列表项的数据并未改变,它们对应的组件也可能被重新渲染。这会带来不必要的性能开销,尤其是在列表项数量较多或组件结构复杂时,用户体验会明显下降。
考虑以下场景:一个卡片组件列表,用户可以添加或移除卡片。当添加一张新卡片时,我们期望只有新卡片被渲染,而现有卡片保持不变;当移除一张卡片时,期望被移除的卡片消失,其他卡片不受影响。然而,由于父组件的数组状态更新,导致整个列表重新渲染,所有卡片组件的渲染逻辑都会被触发,即使它们的数据没有实际变化。
以下是一个简化的示例代码,展示了这个问题:
import React, { useState } from "react"; import "./styles.css"; const fakeData1 = { Card1: [1, 2, 3, 4] }; const fakeData2 = { Card2: [5, 6, 7, 8] }; export default function App() { const [cardArray, setCardArray] = useState(fakeData1); // 初始状态只有一张卡片 const addCard = () => { // 添加一张新卡片 setCardArray((entityState) => ({ ...entityState, fakeData2 // 添加fakeData2 })); }; const Card = ({ id, item }) => { // 每次渲染都会打印,用于观察重渲染情况 console.log("Rendering Card: ", item); const handleRemove = () => { // 移除卡片 setCardArray((entityState) => { const updatedData = { ...entityState }; delete updatedData["fakeData2"]; // 移除fakeData2 return updatedData; }); }; return ( <div style={{ border: "black solid 2px", padding: "50px 0", margin: "10px" }}> <h1>Card - {id}</h1> <div>Content: {Object.values(item)}</div> <button onClick={handleRemove}>Remove Card2</button> </div> ); }; return ( <div className="App"> <button onClick={addCard}>Add Card2</button> {Object.values(cardArray) .flat() .map((item, index) => { // 注意:此处使用 index 作为 key 是不推荐的,下文会详细解释 return <Card id={index} key={index} item={item} />; })} </div> ); }
在上述代码中,当点击“Add Card2”按钮时,cardArray状态更新,导致App组件重新渲染。由于Card组件是App组件的子组件,并且在map中迭代生成,即使Card1的数据没有变化,其console.log("Rendering Card: ", item)也会再次打印,表明它被重新渲染了。
2. 解决方案:利用 React.memo 优化组件渲染
为了避免不必要的子组件重渲染,React 提供了 React.memo 这个高阶组件(Higher-Order Component, HOC)。React.memo 会对组件的 props 进行浅层比较,如果 props 没有发生变化,则跳过该组件的重新渲染,直接复用上一次的渲染结果。
将 Card 组件包装在 React.memo 中,可以有效地阻止其在 props 未变时重渲染:
import React, { useState, memo } from "react"; // 引入 memo import "./styles.css"; const fakeData1 = { Card1: [1, 2, 3, 4] }; const fakeData2 = { Card2: [5, 6, 7, 8] }; // 使用 React.memo 包装 Card 组件 const Card = memo(({ id, item, setCardArray }) => { console.log("Rendering Card: ", item); // 观察重渲染情况 const handleRemove = () => { setCardArray((entityState) => { const updatedData = { ...entityState }; delete updatedData["fakeData2"]; return updatedData; }); }; return ( <div style={{ border: "black solid 2px", padding: "50px 0", margin: "10px" }}> <h1>Card - {id}</h1> <div>Content: {Object.values(item)}</div> <button onClick={handleRemove}>Remove Card2</button> </div> ); }); export default function App() { const [cardArray, setCardArray] = useState(fakeData1); const addCard = () => { setCardArray((entityState) => ({ ...entityState, fakeData2 })); }; return ( <div className="App"> <button onClick={addCard}>Add Card2</button> {Object.values(cardArray) .flat() .map((item, index) => { // 注意:setCardArray 作为 prop 传递给 Card,因为它在 Card 内部被使用 // 这里的 key 仍然是 index,下文会进一步优化 return <Card setCardArray={setCardArray} id={index} key={index} item={item} />; })} </div> ); }
通过上述修改,当点击“Add Card2”按钮时,Card1组件将不再重渲染,只有新添加的Card2组件会渲染。当点击“Remove Card2”时,Card2组件会被卸载,Card1组件也不会重渲染。console.log的输出将验证这一行为。
注意事项:
- props 传递: 如果子组件(如 Card)需要修改父组件(如 App)的状态,那么管理状态的更新函数(如 setCardArray)需要作为 prop 传递给子组件。useState返回的set函数是稳定的,因此将其作为prop传递不会导致React.memo失效。
- 浅层比较: React.memo 默认执行的是 props 的浅层比较。这意味着如果 prop 是一个对象或数组,并且它的引用发生了变化(即使内容没变),React.memo 仍然会认为 prop 发生了变化,从而导致组件重渲染。在这种情况下,可能需要配合 useCallback 来 memoize 函数 prop,以及 useMemo 来 memoize 对象或数组 prop。
3. key 属性的重要性与优化
在React中渲染列表时,key 属性是至关重要的。它帮助React识别哪些列表项被添加、移除或重新排序,从而进行高效的DOM更新。
为什么不推荐使用 index 作为 key?
在上述示例中,我们使用了 index 作为 key。对于静态列表或列表项顺序不会改变的场景,使用 index 勉强可行。但对于动态列表(如添加、移除、重新排序),使用 index 作为 key 会导致以下问题:
- 性能问题: 当列表项的顺序发生变化时(例如在中间插入或删除一个元素),所有后续元素的 index 都会改变。React会认为这些是新的组件,导致它们全部重新渲染,而不是仅仅移动或更新真正改变的组件。
- 状态问题: 如果列表项是带有内部状态的组件,当它们的位置发生变化时,React可能会错误地复用或销毁组件,导致内部状态混乱或丢失。
正确的 key 使用原则:
- 唯一性: key 必须在同级列表中是唯一的。
- 稳定性: key 应该是数据项的唯一标识符,且在数据生命周期内保持不变。通常,后端返回的数据会包含一个唯一的ID(如 id 或 uuid),这是最佳的 key 选择。
- 避免使用 index: 除非你确定列表是静态的且不会发生任何增删改排序。
在我们的示例中,fakeData1 和 fakeData2 结构简单,没有明确的唯一ID。如果数据结构允许,我们应该为每个卡片数据分配一个唯一的ID。例如:
const fakeData1 = { Card1: { id: "card-1", data: [1, 2, 3, 4] } }; const fakeData2 = { Card2: { id: "card-2", data: [5, 6, 7, 8] } };
然后,在 map 中使用这个唯一的 id 作为 key:
// ... {Object.values(cardArray) .flat() .map((item) => { // 假设 item 是 { id: "unique-id", data: [...] } const cardId = Object.keys(item)[0]; // 这里用卡片名称作为key,但更推荐数据自带唯一id return <Card setCardArray={setCardArray} id={cardId} key={cardId} item={item} />; })} // ...
在当前示例的fakeData结构中,item实际上是{ Card1: [...] }或{ Card2: [...] }。我们可以用Object.keys(item)[0](即"Card1"或"Card2")作为相对稳定的key,因为它在卡片生命周期内是固定的,并且在当前数据集中是唯一的。
4. 总结
优化React列表的渲染性能是前端开发中的常见挑战。通过结合使用 React.memo 和正确管理 key 属性,我们可以显著减少不必要的组件重渲染,从而提升应用的性能和用户体验。
- React.memo: 适用于功能组件,通过浅层比较 props 来避免不必要的重渲染。当组件的 props 没有变化时,React.memo 会阻止组件的重新渲染。
- key 属性: 对于动态列表至关重要,它帮助 React 准确识别列表项的变化。始终使用稳定、唯一的 key,并避免在动态列表中使用 index 作为 key。
在实际项目中,请根据组件的特点和数据结构,合理选择并应用这些优化策略,以构建高性能的React应用。
以上就是《React列表优化:避免元素重复渲染技巧》的详细内容,更多关于的资料请关注golang学习网公众号!

- 上一篇
- JS类的作用与适用场景解析

- 下一篇
- SpringCloudConfig版本管理方法
-
- 文章 · 前端 | 6小时前 |
- HTML路径怎么写?相对与绝对路径区别详解
- 277浏览 收藏
-
- 文章 · 前端 | 6小时前 | overflow属性 table-layout:fixed CSS表格高度 height属性 百分比高度
- CSS表格高度控制技巧全解析
- 240浏览 收藏
-
- 文章 · 前端 | 6小时前 |
- JavaScript动态价格计算器实现教程
- 117浏览 收藏
-
- 文章 · 前端 | 6小时前 |
- Ping属性追踪用户行为方法解析
- 104浏览 收藏
-
- 文章 · 前端 | 6小时前 |
- textContent获取或设置节点及其后代的文本内容,常用于动态更新页面文字。
- 458浏览 收藏
-
- 文章 · 前端 | 7小时前 |
- JavaScript数组垃圾回收机制解析
- 315浏览 收藏
-
- 文章 · 前端 | 7小时前 |
- 自定义HTML列表符号技巧分享
- 477浏览 收藏
-
- 文章 · 前端 | 8小时前 | JS 空值合并
- JS空值合并操作符用法解析
- 393浏览 收藏
-
- 文章 · 前端 | 8小时前 |
- D3.js拖拽力图教程详解
- 253浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 514次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 499次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 484次学习
-
- AI Mermaid流程图
- SEO AI Mermaid 流程图工具:基于 Mermaid 语法,AI 辅助,自然语言生成流程图,提升可视化创作效率,适用于开发者、产品经理、教育工作者。
- 243次使用
-
- 搜获客【笔记生成器】
- 搜获客笔记生成器,国内首个聚焦小红书医美垂类的AI文案工具。1500万爆款文案库,行业专属算法,助您高效创作合规、引流的医美笔记,提升运营效率,引爆小红书流量!
- 213次使用
-
- iTerms
- iTerms是一款专业的一站式法律AI工作台,提供AI合同审查、AI合同起草及AI法律问答服务。通过智能问答、深度思考与联网检索,助您高效检索法律法规与司法判例,告别传统模板,实现合同一键起草与在线编辑,大幅提升法律事务处理效率。
- 248次使用
-
- TokenPony
- TokenPony是讯盟科技旗下的AI大模型聚合API平台。通过统一接口接入DeepSeek、Kimi、Qwen等主流模型,支持1024K超长上下文,实现零配置、免部署、极速响应与高性价比的AI应用开发,助力专业用户轻松构建智能服务。
- 208次使用
-
- 迅捷AIPPT
- 迅捷AIPPT是一款高效AI智能PPT生成软件,一键智能生成精美演示文稿。内置海量专业模板、多样风格,支持自定义大纲,助您轻松制作高质量PPT,大幅节省时间。
- 235次使用
-
- 优化用户界面体验的秘密武器:CSS开发项目经验大揭秘
- 2023-11-03 501浏览
-
- 使用微信小程序实现图片轮播特效
- 2023-11-21 501浏览
-
- 解析sessionStorage的存储能力与限制
- 2024-01-11 501浏览
-
- 探索冒泡活动对于团队合作的推动力
- 2024-01-13 501浏览
-
- UI设计中为何选择绝对定位的智慧之道
- 2024-02-03 501浏览