You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 17:21:34