React如何基于对象数组简洁实现多层嵌套列表组件
扁平分类数组转多层嵌套列表实现方案
针对你描述的场景,核心优化思路是把数据结构转换和UI渲染逻辑解耦,不要在渲染阶段嵌套遍历原数组找子节点,先一次性把扁平数组转换成标准树形结构,再用递归组件渲染,代码简洁度和性能都会比嵌套map/filter的写法好很多。
一、原生JS扁平数组转树形结构(O(n)时间复杂度)
你之前用嵌套map/filter的写法本质是每渲染一层就遍历一次全量数据找对应子节点,时间复杂度是O(n*h)(h为树的最大深度),数据量越大、层级越深性能损耗越明显,而且逻辑和渲染绑定很难维护。
推荐用哈希表做中转,仅需2次遍历就能完成树形结构转换,500+条数据的处理耗时在毫秒级:
/** * 扁平数组转嵌套树形结构 * @param {Array} flatList 原始扁平数据数组 * @param {number|string} rootParentValue 根节点的parent字段值,你的场景里是0 * @returns {Array} 树形结构数组,每个节点带children字段存子节点 */ function buildTree(flatList, rootParentValue = 0) { const nodeMap = new Map() // 第一次遍历:建立id到节点的映射,提前初始化children数组 flatList.forEach(item => { nodeMap.set(item.categoryId, { ...item, children: [] }) }) const tree = [] // 第二次遍历:将所有节点挂载到对应父节点下 nodeMap.forEach(node => { const parent = nodeMap.get(node.parent) if (parent) { parent.children.push(node) } else if (node.parent === rootParentValue) { // 匹配到根节点直接放入树的顶层 tree.push(node) } }) // 如需按名称、排序字段排序,可在此处统一处理各层级节点顺序 return tree }
转换后的结构和你预期的嵌套逻辑完全一致,每个节点的children字段就是它的直属子节点数组,不需要后续再做过滤查找。
二、React侧渲染方案:递归组件是这类场景的最优选择
树形结构的渲染天然适合用递归实现,只要做好递归终止条件(节点无有效子节点时停止递归),代码非常简洁,后续加展开收起、节点点击事件等功能也很好扩展。
React 18下的实现代码如下:
// 递归渲染单个树节点 const TreeNode = React.memo(function TreeNode({ node }) { return ( <li> {node.name} {/* 存在子节点时才递归渲染下一层,递归终止条件就是children为空 */} {node.children.length > 0 && ( <ul style={{ paddingLeft: '2em', listStyle: 'none' }}> {node.children.map(child => ( <TreeNode key={child.categoryId} node={child} /> ))} </ul> )} </li> ) }) // 嵌套列表外层组件 function CategoryNestedList({ rawData }) { // 用useMemo缓存树形结构,仅当原始数据变化时才重新转换,避免重复计算 const categoryTree = React.useMemo(() => buildTree(rawData), [rawData]) return ( <ul style={{ listStyle: 'none' }}> {categoryTree.map(rootNode => ( <TreeNode key={rootNode.categoryId} node={rootNode} /> ))} </ul> ) }
渲染出来的缩进、层级结构和你给出的预期效果完全一致。
三、针对500+条数据的优化建议
- 数据里自带的
lvl字段可以直接用来做层级样式区分、缩进控制,不需要渲染时额外计算节点深度 - 如果不需要默认展开所有层级,给TreeNode加
expanded状态控制子节点的挂载,点击展开时再渲染对应子树,能大幅降低首屏渲染的DOM节点数量 - 递归组件用
React.memo包裹,避免父组件更新时所有子节点无意义重渲染 - 树形结构转换逻辑纯JS实现,可以单独抽成工具函数复用,和UI层完全解耦,方便单独测试逻辑正确性
为什么不推荐嵌套map/filter的写法
- 性能差:每一层级渲染都要遍历全量数据找子节点,节点数多了之后性能损耗会非常明显
- 可维护性差:嵌套层级越深代码缩进越深,逻辑缠绕,后续加功能、改逻辑很容易出bug
- 复用性差:数据处理逻辑和渲染逻辑绑定,没法单独把转换后的树形数据用在下拉选择、级联选择等其他场景
内容的提问来源于stack exchange,提问作者Arssin
相关产品推荐
相关产品推荐

