Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[tree view] Add Lazy loading for tree items #9687

Closed
ZNackasha opened this issue Aug 19, 2019 · 13 comments · Fixed by #15308
Closed

[tree view] Add Lazy loading for tree items #9687

ZNackasha opened this issue Aug 19, 2019 · 13 comments · Fixed by #15308
Assignees
Labels
component: tree view TreeView, TreeItem. This is the name of the generic UI component, not the React module! feature: Server integration Better integration with backends, e.g. data source new feature New feature or request plan: Pro Impact at least one Pro user

Comments

@ZNackasha
Copy link

ZNackasha commented Aug 19, 2019

Summary 💡

Add the ability for TreeView to simply ask for the children of a node when a node is expanded. This will help users who have a very large tree and don't have the ability to grab all the data.

Examples 🌈

I'm proposing the addition of three callbacks props to TreeView.
isNodeExpandable called with the nodeId to determine if a node can be expanded
onNodeCollapsed is called when a nodeId that is collapsed.
onNodeExpanded called with the nodeId that is expanded. If the user returns nodes then they are treated as the children of the nodeId.

Motivation 🔦

I'm making a file lookup tree dialog for a project and did not want to request every single file/directory on the server since there is a large amount. Having isNodeExpandable allows for a quick is directory request without having to grab all the files in the directory.

@ZNackasha ZNackasha changed the title Allow TreeView to act like a a virtual component [TreeView] Allow TreeView to act like a a virtual component Aug 20, 2019
@oliviertassinari oliviertassinari added component: tree view TreeView, TreeItem. This is the name of the generic UI component, not the React module! new feature New feature or request labels Aug 20, 2019
@oliviertassinari oliviertassinari changed the title [TreeView] Allow TreeView to act like a a virtual component [TreeView] Lazy load tree items Nov 13, 2019
@oliviertassinari
Copy link
Member

oliviertassinari commented Nov 13, 2019

@KamalAman shares an interesting solution in mui/material-ui#17479 (comment).

@EloB
Copy link

EloB commented Feb 12, 2020

Is there any action on this one? I'm trying to implement a file tree view for a electron project so lazy loading is pretty necessary. 😄

@oliviertassinari
Copy link
Member

oliviertassinari commented Feb 12, 2020

@EloB The component lazy mount its children in the DOM. I would expect that you can create 10,000 TreeItem React elements with almost no performance implication. Is it accurate?

However, the data fetching part might be an issue. We might be able to improve the component to provide lower-level primitives to handle it. For instance, a loading state as well as an event to trigger the data fetching? What are you struggling with?

With @joshwooding, we were talking of making a high-level version of the component, for enterprise, that can query from REST, GraphQL or OData outside of the box. This seems like a downstream problem we would need to fix.

@EloB
Copy link

EloB commented Feb 15, 2020

@oliviertassinari Thanks for your response. I think my problem was that I made a component that returned a React fragment of TreeItem's. I think that is broken at the moment. Using component that returns a TreeItem as it children then I think it works.

<TreeView>
  <Branch path={pathOnDisk} />
</TreeView>

Like this the key navigation was not working etc.

const Branch = () => (
  <>
    <TreeItem nodeId={1} label="One" />
    <TreeItem nodeId={2} label="Two" />
  </>
)

@hibearpanda
Copy link

hibearpanda commented Feb 17, 2020

We currently have a file tree view in our application as well and I tried replacing our hand-rolled tree view with the Material-UI TreeView component.

There was really only 1 problem I had using TreeView in its current state.

Because there is no way to indicate whether a node is expandable or not (Folder or File), and the expandIcon won't appear until a TreeItem has children, I had to lazy load contents for each folder once they appear in the tree, not based on some event or callback.

So, for each Folder node that appears, I would have to make a request for each of them in order for the user to be able to expand each Folder that appears further. This causes the expandIcon to appear asynchronously once the TreeItems would re-render and the children of each Folder is loaded.

Ultimately I think it's less about the performance of rendering many TreeItems, but rather that it is necessary to a) indicate whether a TreeItem can be expanded or not and b) an event / callback when a TreeItem is expanded.

Hope this helps - I'm happy to provide an example if anyone would like one.

Visually speaking the component looks great though!

@EloB
Copy link

EloB commented Feb 17, 2020

@hibearpanda I've also had problem with that. For instance if a directory was empty then it wasn't expandable. I've solved that with adding a TreeItem and wrote "Empty" as label. Except from that I'm fine with the implementation.

@heri16
Copy link

heri16 commented Jan 20, 2021

Has there been a solution or workaround for this issue? Looks like a suitable candidate for deferred/lazy loading would be onNodeToggle?

https://github.com/mui-org/material-ui/blob/42954448866f204b0cf7e35127d5d0af9c24d6fb/packages/material-ui-lab/src/TreeView/TreeView.js#L630

@kishore10x
Copy link

kishore10x commented Mar 9, 2021

We currently have a file tree view in our application as well and I tried replacing our hand-rolled tree view with the Material-UI TreeView component.

There was really only 1 problem I had using TreeView in its current state.

Because there is no way to indicate whether a node is expandable or not (Folder or File), and the expandIcon won't appear until a TreeItem has children, I had to lazy load contents for each folder once they appear in the tree, not based on some event or callback.

So, for each Folder node that appears, I would have to make a request for each of them in order for the user to be able to expand each Folder that appears further. This causes the expandIcon to appear asynchronously once the TreeItems would re-render and the children of each Folder is loaded.

Ultimately I think it's less about the performance of rendering many TreeItems, but rather that it is necessary to a) indicate whether a TreeItem can be expanded or not and b) an event / callback when a TreeItem is expanded.

Hope this helps - I'm happy to provide an example if anyone would like one.

Visually speaking the component looks great though!

I am trying to do something similar and I cannot seem to get the second level TreeItems to display when I am lazy loading the children at each level. Here is a demo with the issue I am facing.
https://codesandbox.io/s/material-demo-forked-zd4fe?file=/demo.js.
Any pointers on where the issue could be?

@JeremyGrieshop
Copy link

JeremyGrieshop commented May 27, 2021

The way I implemented this recently is for all tree items to initially have one child, which just a TreeViewItem displaying a Spinner, indicating it's loading. Then, the when the node is expanded, I go ahead and fetch the items I need for that node and replace its children with its actual children.

image

@Arulpiruthiviraj
Copy link

Arulpiruthiviraj commented Aug 13, 2021

my approach was to iterate with parent elements on each node expandsion

const Tree = ({ listToRender }) => {
  const [childNodes, setChildNodes] = useState([]);
  const [expandedData, setExpandedData] = useState([]);
  const [expanded, setExpanded] = useState([]);
  const [nodeDataLoading, setNodeDataLoading] = useState(false);
  const [currentlyExpandingNode, setCurrentlyExpandingNode] = useState(null);
  useEffect(() => {
    (async () => {
      setChildNodes(listToRender);
      setExpandedData(listToRender);
    })();
  }, [listToRender]);

  /**
   * Transform flat array of objects into tree.
   * @param {(Array|Object)} dataset Tree items.
   * @returns {JSX.Element} Element.
   */
  const toTree = (dataset) => {
    const hashTable = Object.fromEntries(
      dataset.map((aData) => [aData.id, { ...aData, children: [] }])
    );

    const dataTree = [];

    dataset.forEach((aData) => {
      if (aData.parents[0])
        hashTable[aData.parents[0]].children.push(hashTable[aData.id]);
      else dataTree.push(hashTable[aData.id]);
    });

    return dataTree;
  };

  /**
   * Render a tree.
   * @param {Event} event Toggle event.
   * @param {(Array|Object)} nodes Tree items.
   */
  const handleChange = async (event, nodes) => {
    const [nodeId] = nodes.filter((x) => !expanded.includes(x));

    setNodeDataLoading(true);
    setCurrentlyExpandingNode(nodeId);

    if (nodeId) {
      const fetchChildren = await TreeAPI.get();

      const uniqueExpandedData = [...expandedData, ...fetchChildren].filter(
        (
          (
            set // store the set and return the actual callback
          ) =>
          (o) =>
            set.has(o.id) ? false : set.add(o.id)
        )(new Set()) // use an IIFE to create a Set and store it set
      );

      setExpandedData(uniqueExpandedData);

      setChildNodes(await toTree(uniqueExpandedData));
      setNodeDataLoading(false);
    }

    setExpanded(nodes);
    return (
      <>
        <TreeView
          className={classes.tree}
          defaultCollapseIcon={
            <MaterialIcon icon="expand_more" label="Collapse" />
          }
          defaultExpandIcon={
            <MaterialIcon icon="chevron_right" label="Expand" />
          }
          onNodeToggle={handleChange}
        >
          {renderTree(childNodes)} //children rendering
        </TreeView>
      </>
    );
  };
};

@oliviertassinari oliviertassinari transferred this issue from mui/material-ui Jul 15, 2023
@oliviertassinari oliviertassinari changed the title [TreeView] Lazy load tree items [TreeView] Add Lazy loading for tree items Jul 15, 2023
@joserodolfofreitas joserodolfofreitas added the plan: Pro Impact at least one Pro user label Jul 17, 2023
@bigrivi
Copy link

bigrivi commented Dec 29, 2023

The package mui-lazy-tree-view I implemented to solve this problem.

@oliviertassinari
Copy link
Member

In https://mui.com/blog/mui-x-v8-alpha-zero/ we communicated that MUI X v8.0 aims to include this issue.

@oliviertassinari oliviertassinari changed the title [TreeView] Add Lazy loading for tree items [tree view] Add Lazy loading for tree items Mar 1, 2025
Copy link

github-actions bot commented Mar 3, 2025

This issue has been closed. If you have a similar problem but not exactly the same, please open a new issue.
Now, if you have additional information related to this issue or things that could help future readers, feel free to leave a comment.

Note

@ZNackasha How did we do? Your experience with our support team matters to us. If you have a moment, please share your thoughts in this short Support Satisfaction survey.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
component: tree view TreeView, TreeItem. This is the name of the generic UI component, not the React module! feature: Server integration Better integration with backends, e.g. data source new feature New feature or request plan: Pro Impact at least one Pro user
Projects
Status: In progress now