Javascript 动态添加到嵌套数组

Javascript 动态添加到嵌套数组,javascript,reactjs,Javascript,Reactjs,我试图在react中构建一个treeview组件,其中基于用户扩展的节点获取树的数据 想法 当第一个节点被扩展时,一个HTTP请求被发送到一个服务,该服务返回该节点的所有子节点。当另一个节点被扩展时,该节点的子节点被获取,等等。我有一个非常大的数据集,所以我更喜欢这种获取方法,而不是在网站启动时获取所有数据 问题 这可能是扩展部门节点时服务返回的数据示例 { "division": { "id": "1234", "name": "string", "address": "string"

我试图在react中构建一个treeview组件,其中基于用户扩展的节点获取树的数据

想法

当第一个节点被扩展时,一个HTTP请求被发送到一个服务,该服务返回该节点的所有子节点。当另一个节点被扩展时,该节点的子节点被获取,等等。我有一个非常大的数据集,所以我更喜欢这种获取方法,而不是在网站启动时获取所有数据

问题

这可能是扩展部门节点时服务返回的数据示例

{
 "division": {
 "id": "1234",
 "name": "string",
 "address": "string",
 },
 "children": [
   {
    "id": "3321",
    "parentId": "1234",
    "name": "Marketing",
    "address": "homestreet",
   },
   {
    "id": "3323",
    "parentId": "1234",
    "name": "Development",
    "address": "homestreet",
   }
 ]
}
然后我可以扩展开发节点并获取该节点的子节点


我想我需要某种嵌套数组,但我不确定如何处理保持数组的正确顺序,以便获得正确的树层次结构。因为用户可以选择展开任何节点。有人能帮忙吗

我对React不是特别熟悉,但一般来说,每个节点都必须有一个
子元素
元素,这将是一个空数组。我假设当用户展开一个节点时,您知道他们正在展开哪个节点(当用户单击展开按钮时,节点对象可能对您可用),因此获取子节点并用服务器的数据替换空的
子节点
数组是很简单的。

我想我的原始答案是(以下,因为它已经被接受)是一个反模式或没有思考问题的例子

React组件树本身就是一棵树。因此,我认为如果你有一个
TreeNode
组件或类似的组件,知道如何加载它的子组件,这是很好的,比如:

function TreeNode({id, name, parentId, address}) {
    // The nodes, or `null` if we don't have them yet
    const [childNodes, setChildNodes] = useState(null);
    // Flag for whether this node is expanded
    const [expanded, setExpanded] = useState(false);
    // Flag for whether we're fetching child nodes
    const [fetching, setFetching] = useState(false);
    // Flag for whether child node fetch failed
    const [failed, setFailed] = useState(false);

    // Toggle our display of child nodes
    const toggleExpanded = useCallback(
        () => {
            setExpanded(!expanded);
            if (!expanded && !childNodes && !fetching) {
                setFailed(false);
                setFetching(true);
                fetchChildNodes(id)
                .then(nodes => setChildNodes(nodes.map(node => <TreeNode {...node} />)))
                .catch(error => setFailed(true))
                .finally(() => setFetching(false));
            }
        },
        [expanded, childNodes, fetching]
    );

    return (
        <div class="treenode">
            <input type="button" onClick={toggleExpanded} value={expanded ? "-" : "+"} style={{width: "28px"}}/>
            <span style={{width: "4px", display: "inline-block"}}></span>{name}
            {failed && expanded && <div className="failed">Error fetching child nodes</div>}
            {fetching && <div className="loading">Loading...</div>}
            {!failed && !fetching && expanded && childNodes && (childNodes.length > 0 ? childNodes : <div class="none">(none)</div>)}
        </div>
    );
}
这包括任何“ajax”操作失败的概率为十分之一,因此可以进行测试。只需折叠并再次展开即可重试

我会让树的每个节点成为一个单独的组件,称为“TreeNode”或其他什么,那么它必须包含的是:

  • 其父节点的ID(如果是根节点,则可以为空)
  • 它的子ID列表
  • 显示是否展开的布尔属性
  • 最后,它自己的负载数据(在您的例子中是名称和地址)
在这方面,“树”组件应仅包含单个属性:

  • 其根节点的ID
然后,当用户单击某个TreeNode时,它将触发一个事件,TreeNode将处理该事件:

  • 若它还并没有展开,那个么它将开始展开,这将导致获取请求
  • 如果它已经被“扩展”——那么它取决于您的需求——它可以被折叠,也可以不折叠——无论它是否适合您的用例
此设置意味着您只需要两种类型的组件,并且不需要任何嵌套阵列

每个TreeNode组件负责如何呈现其自己的有效负载数据,然后呈现其所有子组件,因此它必须知道如何应用视觉样式等。但这与任何可见组件都是一样的

因此,关键是每个节点只负责一个更深层次,即它的直接子节点,而对孙子辈的情况保持不可知论,等等

更新:

一个小警告:每个TreeNode都必须阻止鼠标点击向上冒泡。这里有一个专门的问题,所以:

更新2

另外,有两种方法可以保存每个节点的回迁数据:

  • 在父组件内保存所有子数据,包括有效负载(不仅仅是ID)。在这种情况下,上面的“子ID列表”将转到“子数据列表:ID和有效负载”

  • 所有获取的数据都被“全局”保存,例如在树组件内部。然后,在呈现子节点时,每个树节点都必须引用该知识源并通过ID检索数据

  • 这种情况下的树组件可以使用JS映射对象(ID->NodeData)


    在第二种设置中,如果不使用树组件,每个树节点也应该保留对树或直接映射的引用。类似的情况。

    我的第一个想法是,每个节点都有自己的状态。你是需要应用程序中的数据,还是可以单独驻留在节点中?我不确定我是否理解你的问题。你能重新措辞吗?谢谢你的回答。你所说的id路径是什么意思?@John-我一直想在某处定义它。:-)我指的是一个数组,每个节点的id指向目标节点。对于
    开发
    ,这将是
    [“1234”,“3323”]
    。但是你不必这样做,你可以稍后通过
    id
    找到路径,它只需要搜索。再次感谢:)因为我是新手,请确保你引用的
    树节点是组件的名称?@John-一个你在代码中定义的组件,是的。还有一些我应该提到的事情。:-)@约翰-事实上,我想知道这个答案是否是反模式的。DOM/React组件树已经是一个树。看起来
    TreeNode
    应该直接处理其子节点的更新,而
    TreeView
    组件本质上应该只是根节点的容器。