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

React递归树组件:实现节点选中及点击添加子节点功能

解决方案

1. 实现节点选中功能

先在App组件中添加状态存储选中节点ID,再将状态传递给Tree组件,最后在节点元素上绑定点击事件并根据选中状态设置样式:

修改App组件:

function App() {
  const [data, setData] = useState([{
    id: 1,
    name: "node 1",
    children: [{
      id: 2,
      name: "node 1.1",
      children: [{
        id: 8,
        name: "node 1.1.1",
        children: []
      }]
    },{
      id: 3,
      name: "node 1.2",
      children: [{
        id: 4,
        name: "node 1.2.1",
        children: [{
          id: 5,
          name: "node 1.2.1.1",
          children: []
        }]
      }]
    }]
  }])
  // 添加选中节点状态
  const [selectedNodeId, setSelectedNodeId] = useState(null);

  return (
    <div className="App">
      <Tree 
        data={data} 
        setData={setData} 
        margin={15}
        selectedNodeId={selectedNodeId} // 传递选中ID
        setSelectedNodeId={setSelectedNodeId} // 传递设置选中状态的函数
      />
      <button onClick={addChildToSelected} disabled={!selectedNodeId}>Add child</button>
    </div>
  );
}

修改Tree组件:

const Tree = ({data, margin, setData, selectedNodeId, setSelectedNodeId}) => {
    return (
        <Fragment>
            {data.map((element, index) => (
             <div key={element.id}> {/* 添加key避免React警告 */}
                 <div className="tier">
                    <div
                        style={{
                            marginLeft: `${margin}px`,
                            backgroundColor: selectedNodeId === element.id ? '#e0e0e0' : 'transparent',
                            padding: '4px 8px',
                            cursor: 'pointer',
                            borderRadius: '4px',
                            transition: 'background-color 0.2s'
                        }}
                        onClick={() => setSelectedNodeId(element.id)} // 点击设置选中状态
                    >
                        {element.name}
                    </div>
                 </div>
                {element.children && element.children.length ? (
                    <Tree
                        data={element.children}
                        setData={setData}
                        margin={margin + 15}
                        selectedNodeId={selectedNodeId}
                        setSelectedNodeId={setSelectedNodeId}
                    />
                ) : null}
             </div>))}
        </Fragment>
    );
};

2. 实现选中节点添加子节点功能

编写递归函数遍历树结构,找到选中节点并添加新子节点,同时保证节点ID唯一:

在App组件中添加辅助函数和添加子节点函数:

function App() {
  // ... 原有代码 ...

  // 辅助函数:遍历树结构获取最大ID,生成唯一新ID
  const getNextId = (nodes) => {
    let maxId = 0;
    const traverse = (nodeList) => {
      nodeList.forEach(node => {
        if (node.id > maxId) maxId = node.id;
        if (node.children.length > 0) traverse(node.children);
      });
    };
    traverse(nodes);
    return maxId + 1;
  };

  // 添加子节点到选中节点的函数
  const addChildToSelected = () => {
    if (!selectedNodeId) return;

    // 递归遍历并更新节点
    const updateTree = (nodes) => {
      return nodes.map(node => {
        if (node.id === selectedNodeId) {
          // 找到选中节点,添加新子节点
          const newChildId = getNextId(data);
          const newChildName = `node ${node.name.split(' ')[1]}.${node.children.length + 1}`;
          return {
            ...node,
            children: [...node.children, { id: newChildId, name: newChildName, children: [] }]
          };
        }
        // 递归处理子节点
        if (node.children.length > 0) {
          return { ...node, children: updateTree(node.children) };
        }
        return node;
      });
    };

    setData(updateTree(data));
  };

  // ... 原有return代码 ...
}

关键说明

  • 选中状态管理:通过selectedNodeId状态跟踪当前选中节点,点击节点时更新该状态,同时通过内联样式动态切换选中样式。
  • 递归更新树结构:使用不可变更新(通过扩展运算符...复制对象/数组)避免直接修改原状态,保证React状态更新的正确性。
  • 唯一ID生成:通过遍历整个树结构获取最大ID,确保新节点ID不重复。
  • 按钮可用性:添加disabled属性,当没有选中节点时按钮不可点击,提升用户体验。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 03:20:43