Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fetching Async TreeView Data

I'm new to react and I'm building a TreeView component in react where I fetch data as nodes are expanded. I'm using the TreeView from Material UI: https://material-ui.com/api/tree-view/

I have created the component below which fetches data when a node is expanded. MyTreeItem is a custom version expanding the original with a fetch call. But I'm confused on how to display the childnodes. How can I do this?

import ReactDOM from "react-dom";
import React from "react";
import TreeView from "@material-ui/lab/TreeView";
import ExpandMoreIcon from "@material-ui/icons/ExpandMore";
import ChevronRightIcon from "@material-ui/icons/ChevronRight";
import TreeItem from "@material-ui/lab/TreeItem";
const { useState, useCallback } = React;

export default function MyTreeItem({ id, name }) {
  const [childNodes, setChildNodes] = useState(null);
  const [expanded, setExpanded] = React.useState([]);

  function fakeAjax(url) {
    return fetch(url).then(res => {
      if (!res.ok) {
        throw new Error("HTTP error " + res.status);
      }
      return res.json();
    });
  }

  function fetchChildNodes(id) {
    // Should in reality be "URL/id", but fake JSON for now
    return fakeAjax(`https://api.myjson.com/bins/1aqhsc`);
  }

  const handleChange = (event, nodes) => {
    const expandingNodes = nodes.filter(x => !expanded.includes(x));
    setExpanded(nodes);
    if (expandingNodes[0]) {
      const childId = expandingNodes[0];
      fetchChildNodes(childId).then(result =>
        setChildNodes(result.children.map(node => <MyTreeItem {...node} />))
      );
    }
  };

  return (
    <TreeView
      defaultCollapseIcon={<ExpandMoreIcon />}
      defaultExpandIcon={<ChevronRightIcon />}
      expanded={expanded}
      onNodeToggle={handleChange}
    >
      {/*The node below should act as the root node for now */}
      <TreeItem nodeId="1" label="Applications">
        {/*The childnodes should be here*/}
      </TreeItem>
    </TreeView>
  );
}
const rootElement = document.getElementById("root");
ReactDOM.render(<MyTreeItem />, rootElement);

like image 964
John Avatar asked Nov 16 '25 08:11

John


1 Answers

In your example, it seems, you are trying to build the tree where each node that has children will also be a tree.

In this case, render will look like this:

{/*The node below should act as the root node for now */}
<TreeItem nodeId={props.id} label={props.name}>
    {childNodes || [<div key="stub" />]} // stub div is used so user could see expand icon
</TreeItem>
// you also need to pass props for root items
ReactDOM.render(<MyTreeItem id="1" name="Applications" />, rootElement);

You can check the working code here: https://codesandbox.io/s/material-demo-k5ol6

There are multiple ways to render a tree. One is to model a tree structure and render it in a way so each node that has children would no longer be a whole another tree with local state, like in your implementation. The advantage of this approach is that you can provide a complete tree via props, not only by fetching it on 'expand' action.

Here's the example of such an approach: https://codesandbox.io/s/material-demo-h6zfe

Also, you could render the tree as a list, where each nested level has an offset specified via styles. This has an advantage when you need to have a pagination on each level separately (in case if a single level has a lot of items). The implementation is not as easy as in the cases above, so I will not provide an example.

like image 89
SciFiThief Avatar answered Nov 18 '25 21:11

SciFiThief