
在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)也会再次打印,表明它被重新渲染了。
为了避免不必要的子组件重渲染,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的输出将验证这一行为。
注意事项:
在React中渲染列表时,key 属性是至关重要的。它帮助React识别哪些列表项被添加、移除或重新排序,从而进行高效的DOM更新。
为什么不推荐使用 index 作为 key?
在上述示例中,我们使用了 index 作为 key。对于静态列表或列表项顺序不会改变的场景,使用 index 勉强可行。但对于动态列表(如添加、移除、重新排序),使用 index 作为 key 会导致以下问题:
正确的 key 使用原则:
在我们的示例中,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,因为它在卡片生命周期内是固定的,并且在当前数据集中是唯一的。
优化React列表的渲染性能是前端开发中的常见挑战。通过结合使用 React.memo 和正确管理 key 属性,我们可以显著减少不必要的组件重渲染,从而提升应用的性能和用户体验。
在实际项目中,请根据组件的特点和数据结构,合理选择并应用这些优化策略,以构建高性能的React应用。
以上就是优化React列表渲染:避免数组元素不必要的重渲染的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号