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

React递归函数getBranches在Chrome调试VM下返回重复数据问题

实现需求

现有任意深度的树状数据结构,需要从中提取所有非叶子分支节点组成列表,作为props传递给子组件,使用的数据集与相关代码如下。

问题描述

运行时返回的分支数组存在重复数据,经排查发现getBranches()函数会被Chrome调试VM额外调用,向已存在的数组中重复追加相同数据,控制台调用计数可参考附图。请问当前实现思路是否存在根本性错误?该如何修复该问题?

树结构数据代码

const treeData = {
'first-level-node-1': {               // 节点key
  label: 'Regija',
  type: TYPES[1],
  index: 0, // 渲染顺序
  url: 'http://opa.com',     
  nodes: {
    'second-level-node-1': {
      label: 'Bolnica BN',
      index: 0,
      type: 1,
      nodes: {
        'third-level-node-1': {
          label: 'Konektor Sysmex',
          index: 0,
          nodes: {}, 
          type: TYPES[0]
        },
      },
    },
  },
},
'first-level-node-2': {
  label: 'Regija 2',
  index: 1,
  type: TYPES[1],
  nodes: {
    '2-1': {
      label: 'Dz Trebinje',
      index: 0,
      type: 1,
      nodes: {
        '3-1': {
          label: 'Konektor Biomerux',
          index: 0,
          hasNodes: false,
          type: TYPES[0],
        }
      }
    }
  }
},
};

递归函数getBranches代码

const getBranches = (treeData) => {
console.log('i= ',i++)
Object.keys(treeData).forEach((key) => {
    if (treeData[key].type!==TYPES[0]) {
        branches.push({key: key, label: treeData[key].label});
        console.log({key: key, label: treeData[key].label})
        treeData[key].nodes && getBranches(treeData[key].nodes)
    };
});
return [...branches];
};

React组件代码

const Sites = () => {

const branches = getBranches(treeData);

console.log(branches);
//const [data, setData] = useState(treeData);

return (
    <Container fluid="md">
        <Row>
            <Col>
                <MyTreeView data={treeData}/>
            </Col>
            <Col>
                <BranchView
                    sites={treeData}
                    parentList={branches}
                />
            </Col>
        </Row>
    </Container>
);
};
问题根因

当前实现存在根本性错误,和Chrome调试VM调用没有直接关系:

  • 核心问题是外部状态污染:branches数组定义在getBranches函数外部,属于闭包/全局共享变量。React组件每次重渲染(包括严格模式下的预执行、调试器的快照执行)都会重新调用getBranches,函数不会重置branches数组,只会不断往已有数组里追加数据,直接导致重复。
  • 递归逻辑无状态隔离:递归过程没有在每次函数调用时维护独立的结果集合,完全依赖外部变量存储结果,函数本身不是纯函数,只要多次执行就必然出现数据累加重复。

所谓Chrome调试VM额外调用只是触发问题的场景,不是根源。哪怕没有调试行为,组件只要触发任意重渲染,都会出现重复数据。

修复方案

把结果数组放到函数内部初始化,递归时合并子层结果,完全消除外部依赖,让函数成为输入输出稳定的纯函数:

const getBranches = (treeData) => {
  // 每次调用时独立初始化当前层结果,完全隔离外部状态
  const currentBranches = [];
  Object.keys(treeData).forEach((key) => {
    const node = treeData[key];
    if (node.type !== TYPES[0]) {
      currentBranches.push({ key, label: node.label });
      // 递归获取子层分支,合并到当前结果
      if (node.nodes) {
        currentBranches.push(...getBranches(node.nodes));
      }
    }
  });
  return currentBranches;
};

额外性能优化:在React函数组件中,建议用useMemo包裹计算逻辑,避免每次重渲染都重复遍历整棵树:

const Sites = () => {
  // 空依赖数组表示仅在组件首次挂载时计算一次
  const branches = useMemo(() => getBranches(treeData), []);

  // 其余组件逻辑保持不变
}

补充说明

分支节点与叶子节点通过节点的type属性区分,函数重复调用计数参考截图:
控制台调用计数截图

内容的提问来源于stack exchange,提问作者D Kuzmanovic

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 04:01:14