如何避免嵌套map以提升React分组列表渲染性能
React分组列表渲染性能优化方案
问题核心原因
首先明确性能问题的两个核心诱因:
- 每次组件重渲染时都会重复执行分组计算、嵌套遍历生成JSX的逻辑,无任何缓存机制,你在return中直接调用渲染函数确实是性能下降的核心原因之一——只要组件触发重渲染,即使
itemSet没有任何变化,分组、遍历逻辑都会完整执行一遍。 - 分组层级的
React.Fragment没有添加唯一key,且你使用了小写开头的<group>、<item>组件(React会将小写开头的标签识别为原生HTML标签,无法正确做组件diff优化),导致React无法复用已有DOM节点,只能全量销毁重建。
优化方案(含消除嵌套映射的实现)
方案1:保留嵌套逻辑但做全链路缓存(改造成本最低)
嵌套map本身不是性能问题,不合理的无缓存重复计算才是,不需要完全消除嵌套,优化点如下:
- 用
useMemo缓存分组结果,仅当itemSet变化时才重新计算分组 - 给分组层级添加唯一key
- 修正组件名大写规范,子组件用
React.memo包裹避免不必要重渲染 - 缓存渲染结果,避免每次重渲染重复生成JSX
优化后代码示例:
import { useMemo } from 'react'; // 组件名必须大写开头,建议用React.memo包裹 const Group = React.memo(({ name }) => <div>{name}</div>); const Item = React.memo(({ id, item }) => <div key={id}>{item}</div>); // 业务组件内逻辑 const YourComponent = () => { const itemSet = [ { id: 1, group: 'A', item : 'someitem' }, { id: 2, group: 'A', item : 'someitem' }, { id: 3, group: 'B', item : 'someitem' }, { id: 4, group: 'B', item : 'someitem' }, ]; // 缓存分组结果,仅itemSet变化时重新计算 const groupedItems = useMemo(() => { return itemSet.reduce((group, item) => { if (!group[item.group]) { group[item.group] = []; } group[item.group].push(item); return group; }, {}); }, [itemSet]); // 缓存渲染的JSX,仅groupedItems变化时重新生成 const renderList = useMemo(() => { return Object.entries(groupedItems).map(([group, items]) => ( // 分组层级必须加唯一key <React.Fragment key={group}> <Group name={group} /> {items.map((item) => ( <Item key={item.id} id={item.id} item={item.item} /> ))} </React.Fragment> )); }, [groupedItems]); return ( <MuiThemeProvider theme={themeDark}> <AppBar /> <CssBaseline /> {renderList} </MuiThemeProvider> ); };
方案2:完全消除嵌套map的实现
如果需要彻底去掉嵌套遍历,可以提前对原始数据做预处理,给每个项插入分组标记,仅用一层map完成渲染:
// 预处理数据,生成带分组标记的展平列表 const flattedItems = useMemo(() => { const sorted = [...itemSet].sort((a, b) => a.group.localeCompare(b.group)); const result = []; let lastGroup = null; sorted.forEach(item => { if (item.group !== lastGroup) { // 插入分组头标记项 result.push({ type: 'group', id: `group_${item.group}`, name: item.group }); lastGroup = item.group; } result.push({ type: 'item', ...item }); }); return result; }, [itemSet]); // 仅一层map完成渲染,无嵌套 const renderList = useMemo(() => { return flattedItems.map(item => { if (item.type === 'group') { return <Group key={item.id} name={item.name} />; } return <Item key={item.id} id={item.id} item={item.item} />; }); }, [flattedItems]);
这个方案可以完全消除嵌套映射,渲染效率和单层遍历一致。
内容的提问来源于stack exchange,提问作者Richardson
相关产品推荐
相关产品推荐

