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

能否使用useState操作树形对象的状态值?思维导图开发问询

问题描述

我正在开发一款思维导图软件,现有如下树形对象nestedData,并已通过useState将其声明为状态data:

const nestedData = {
    name: 'A',
    id: 0,
    parentId: null,
    children: [
        {
            name: 'b',
            id: 1,
            parentId: 0,
            children: []
        },
        {
            name: 'c',
            id: 2,
            parentId: 0,
            children: [
                {
                    name: 'd',
                    id: 3,
                    parentId: 2,
                    children: [
                        {
                            name: 'e',
                            id: 4,
                            parentId: 3,
                            children: []
                        },
                        {
                            name: 'e',
                            id: 4,
                            parentId: 3,
                            children: [
                                {
                                    name: 'e',
                                    id: 5,
                                    parentId: 4,
                                    children: []
                                }
                            ]
                        }
                    ]
                }
            ]
        },
    ],
}
const [data, setData] = useState(nestedData);

请问:

  1. 是否可以使用useState来操作特定子节点的值(如添加子节点、更新文本)?
  2. 如何检测用户点击事件,获取parentId以在父节点下方展示其子节点?
解决方案

一、用useState操作特定子节点

完全可以用useState操作特定子节点,但React状态遵循不可变原则,需要创建原数据的深拷贝,修改拷贝后的目标节点后,再通过setData更新状态。

1. 更新节点文本

示例:修改id=5的节点名称

const updateNodeName = (targetId, newName) => {
  // 递归遍历找到目标节点并返回修改后的新节点
  const updateNode = (node) => {
    if (node.id === targetId) {
      return {...node, name: newName};
    }
    if (node.children) {
      return {...node, children: node.children.map(updateNode)};
    }
    return node;
  };
  setData(prevData => updateNode(prevData));
};

// 调用:将id=5的节点名称改为"f"
updateNodeName(5, "f");

2. 添加子节点

示例:给id=2的节点添加新子节点

const addChildNode = (parentId, newNode) => {
  const addNode = (node) => {
    if (node.id === parentId) {
      return {...node, children: [...node.children, newNode]};
    }
    if (node.children) {
      return {...node, children: node.children.map(addNode)};
    }
    return node;
  };
  setData(prevData => addNode(prevData));
};

// 调用:给id=2的节点添加名为"g"的子节点
addChildNode(2, {name: "g", id: 6, parentId: 2, children: []});

二、处理点击事件控制子节点展示

通过给节点绑定点击事件,维护展开状态集合,即可实现点击父节点展示/隐藏子节点的功能:

1. 维护节点展开状态

添加状态记录哪些节点处于展开状态:

const [expandedNodes, setExpandedNodes] = useState(new Set([0])); // 默认展开根节点

2. 节点点击事件与渲染逻辑

编写递归渲染节点的函数,绑定点击事件控制展开状态:

const renderNode = (node) => {
  const isExpanded = expandedNodes.has(node.id);
  
  return (
    <div key={node.id} onClick={() => handleNodeClick(node.id)}>
      <span>{node.name}</span>
      {/* 仅展开状态下渲染子节点 */}
      {isExpanded && node.children.length > 0 && (
        <div style={{marginLeft: "20px"}}>
          {node.children.map(renderNode)}
        </div>
      )}
    </div>
  );
};

const handleNodeClick = (nodeId) => {
  setExpandedNodes(prev => {
    const newSet = new Set(prev);
    newSet.has(nodeId) ? newSet.delete(nodeId) : newSet.add(nodeId);
    return newSet;
  });
};

3. 渲染整个树形结构

在组件中调用渲染函数:

return <div>{renderNode(data)}</div>;

点击节点时,当前节点的id就是其子节点的parentId,通过切换展开状态即可控制子节点的展示与隐藏。

内容的提问来源于stack exchange,提问作者Al Nahian Ark

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.23 15:54:42