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
相关产品推荐
相关产品推荐

