🌐
GitHub
github.com › mui-org › material-ui › issues › 17064
[tree view] Add Lazy loading for tree items · Issue #9687 · mui/mui-x
August 19, 2019 - 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.
Author   ZNackasha
🌐
GitHub
github.com › mui › material-ui › issues › 17064
[TreeView] Lazy load tree items · Issue #17064 · mui/material-ui
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.
Discussions

[TreeView] Async loading support
The issue is present in the latest release. I have searched the issues of this repository and believe that this is not a duplicate. Current Behavior 😯 When importing a Third Level dependency @mater... More on github.com
🌐 github.com
4
September 18, 2019
Fetching Async TreeView Data - javascript
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 cre... More on stackoverflow.com
🌐 stackoverflow.com
[TreeView] The node takes time to open
The issue is present in the latest release. I have searched the issues of this repository and believe that this is not a duplicate. Current Behavior 😯 We need to display a tree that contains many n... More on github.com
🌐 github.com
5
October 27, 2022
How to set expanded prop for material ui TreeView component conditionally
I am trying to create a tree select component like the one from antd tree-select using material ui. I have a material-ui TextField and TreeView components one below the other. Initially I want the ... More on stackoverflow.com
🌐 stackoverflow.com
🌐
npm
npmjs.com › package › react-lazy-paginated-tree
react-lazy-paginated-tree - npm
June 11, 2019 - export type LoadingProps = { theme: Theme, node: Node, indentWidth: number, depth: number, }; export type DepthPaddingProps = { indentWidth: number, depth: number, children: any, }; MIT. react · tree · treeview · lazyload · pagination · material-ui · npm i react-lazy-paginated-tree ·
      » npm install react-lazy-paginated-tree
    
Published   Jun 11, 2019
Version   0.8.4
Author   Bowei Han
🌐
MUI
mui.com › x › react-tree-view
Tree View React component - MUI X
Requires a commercial license. Better suited for data-rich and enterprise applications, with features such as reordering, lazy loading, and virtualization.
🌐
GitHub
github.com › mui › material-ui › issues › 17479
[TreeView] Async loading support · Issue #17479 · mui/material-ui
September 18, 2019 - The MUI TreeView/TreeItem components are using the /esm/ version of the module, which makes creating a custom TreeItem that that uses the context impossible. And yes, the https://material-ui.com/guides/minimizing-bundle-size/ documentation says
Author   KamalAman
🌐
GitHub
github.com › mui › material-ui › pull › 17046 › files
[TreeView] Lazy render the tree items by Shubhamchinda · Pull Request #17046 · mui/material-ui
mui / material-ui Public · There was an error while loading. Please reload this page. Notifications · You must be signed in to change notification settings · Fork 32.7k · Star 98k · New issue · Merged · oliviertassinari merged 2 commits into mui:master from Shubhamchinda:patch-2 ·
Author   mui
🌐
johnnyreilly
johnnyreilly.com › home › blog › mui react tree view: pass data to treeitem
MUI React Tree View: pass data to TreeItem | johnnyreilly
July 1, 2024 - Learn how to pass arbitrary data to individual nodes in the MUI treeview component so individual nodes can be customised; for instance implementing a loader.
Top answer
1 of 3
10

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.

2 of 3
1

I suggest using a recursive function to build the children. You can see how I have done this in typescript. I have used the includeChildren variable to improve performance by only drawing the items which are on screen.

const displayChildren = (parent: DataItem, isRoot: boolean) => {
    var includeChildren = isRoot;
    if (includeChildren === false && expanded) {
        for (let expandedItem of expanded) {
            const id = parseInt(expandedItem);
            if (id === parent.id || id === parent.parentId) {
                includeChildren = true;
                break;
            }
        }
    }
    const children = includeChildren && childrenLookup?.get(parent.id);
    return (
        <TreeItem key={parent.id} nodeId={'' + parent.id} label={parent.name} >
            {children && children.map((c: DataItem) => displayChildren(c, false))}
        </TreeItem>
    )
}

I have stored the expanded items in react state, as per the Controlled tree view example. You can return the TreeView like this:

return (
    <TreeView
        defaultCollapseIcon={<ExpandMoreIcon />}
        defaultExpandIcon={<ChevronRightIcon />}
        style={{ height: 240, flexGrow: 1, maxWidth: 400, overflowY: 'auto' }}
        expanded={expanded}
        selected={selected}
        onNodeToggle={handleToggle}
        onNodeSelect={handleSelect}
    >
        {dataItems && getRootItems(dataItems).map((parent) => displayChildren(parent, true))}
    </TreeView>)

I found that using a childrenLookup is really quick so you don't need to fetch data from the API. My treeview has 10,000 items and it still works fine.

If you are wondering how the lookup works, this is how I have done it. The data is coming from a mobx store but you could use any API response, just make sure it's only called once.

const [childrenLookup, setChildrenLookup] = React.useState<Map<number, DataItem[]>>();
React.useEffect(() => {
    let lookup = new Map<number, DataItem[]>();
    if (dataStore.dataItems) {
        for (let dataItem of dataStore.dataItems) {
            if (dataItem.parentId) {
                if (!lookup.has(dataItem.parentId)) {
                    lookup.set(dataItem.parentId, []);
                }
                lookup.get(dataItem.parentId)?.push(dataItem);
            }
        }
        setChildrenLookup(lookup);
    }
}, [dataStore.dataItems])
🌐
Syncfusion
ej2.syncfusion.com › react › documentation › treeview › data-binding
Data binding in React TreeView component | Syncfusion
TreeView has load on demand (Lazy load), by default. It reduces the bandwidth size when consuming huge data.
Find elsewhere
🌐
GitHub
github.com › mui › material-ui › issues › 27403
[TreeView] The node takes time to open · Issue #27403 · mui/material-ui
October 27, 2022 - The issue is present in the latest release. I have searched the issues of this repository and believe that this is not a duplicate. Current Behavior 😯 We need to display a tree that contains many nodes, but it takes time to open the node...
Author   Rajaa-BARHTAOUI
🌐
MUI
mui.com › x › react-tree-view › rich-tree-view › lazy-loading
Rich Tree View - Lazy loading - MUI X
Lazy load the data from your Tree View · To dynamically load data from the server, including lazy-loading of children, you must create a data source and pass the dataSource prop to RichTreeView
🌐
GitHub
github.com › bigrivi › mui-lazy-tree-view
GitHub - bigrivi/mui-lazy-tree-view: Implement lazy loading of data for mui tree view
It should always be called, even if there are errors when loading the data. const onLazyLoad = ({ key, children }) => { return new Promise<TreeNode[]>((resolve) => { if (children && children.length) { resolve([]); return; } setTimeout(() => { resolve([ { title: `Another lazy node...`, key: `${key}-0`, children: [], }, { title: "A non-lazy node without children", key: `${key}-1`, }, { title: "A non-lazy node with child nodes", key: `${key}-2`, children: [ { title: "nodeA", key: `${key}-2-1`, }, { title: "nodeB", key: `${key}-2-2`, }, ], }, ]); }, 1000); }); };
Author   bigrivi
Top answer
1 of 1
2

well I create a service with two functions

getRootNodes(){}

and

getChildren(node:any){}

getRootNodes, return the first level of root.

getChildren(nodes:any), return the "children". But, not only return an array of strings, I make this return an array of object with a propertie "isExpandable". For material-tree is necesary know when a children has children

The next change is usign our service in the DinamicDataSource. Our toggleNode must use of the service. if !expands is the code that was under the setTimeOut, Well, we don't need a setTimeOut, of course.

If expand=true, we call to the service and subscribe to the data. INTO the subscribe the code under the setTimeOut.

See that in both case we need call to this.dataChange.next(this.data)

toggleNode(node: DynamicFlatNode, expand: boolean) {
    const index = this.data.indexOf(node);
    if (!expand)
    {
      let count = 0;
        for (let i = index + 1; i < this.data.length
          && this.data[i].level > node.level; i++, count++) {}
        this.data.splice(index + 1, count);
        this.dataChange.next(this.data);
    }
    else
    {
    node.isLoading = true;
    this.dataService.getChildren(node.item).subscribe(children=>{
      if (!children || index < 0) { // If no children, or cannot find the node, no op
        node.isLoading = false;

        return;
      }
      if (expand) {
        const nodes = children.map(obj =>
          new DynamicFlatNode(obj.name, node.level + 1, obj.isExpandable));
        this.data.splice(index + 1, 0, ...nodes);
      }
      node.isLoading = false;
      // notify the change
      this.dataChange.next(this.data);

    })
    }
  }

The last change is pass to a ngOnInit the functions of the constructor in the TreeDynamicExample

ngOnInit()
  {
    this.treeControl = new FlatTreeControl<DynamicFlatNode>(this.getLevel, this.isExpandable);
    this.dataSource = new DynamicDataSource(this.treeControl, this.dataService);
    this.dataService.getRootNodes().subscribe(res=>{
      this.dataSource.data = res.map(name => new DynamicFlatNode(name, 0, true));
    })
  }

See that, before equal this.dataSourceData to a DynamicFlatNode, we need get the data and make it in the subscribe

Update: Demystifying the DinamicData Source.

Well, to understand the tree-dinamic we can take a look at what is "data" in DynamicData Source. This data it's only an array of object with the properties: item,level,expandable and loading. So, it's look like

[
{item: "Fruits",level:0,expandable:true,isLoading:false},
{item: "Apple",level:1,expandable:true,isLoading:false},
{item: "Orange",level:1,expandable:false,isLoading:false},
{item: "Bannana",level:1,expandable:false,isLoading:false},
{item: "Vegetables",level:0,expandable:true,isLoading:false}
]

Yes, it's just a simple array. But it's an array ordered. So we can create a function addNode in our DinamicDataSource, like

  addSubNode(index:number,name:string,isExpandable:boolean)
  {
     const node=this.data[index];
     const dfn={item: name,
         level:node.level+1,
         expandable:isExpandable,
         isLoading:false}
     this.data.splice(index + 1, 0, ...[dfn]);
     this.dataChange.next(this.data);
  }

  addNode(index:number,name:string,isExpandable:boolean)
  {
     const node=this.data[index];
     const dfn={item: name,
         level:node.level,
         expandable:isExpandable,
         isLoading:false}
     this.data.splice(index + 1, 0, ...[dfn]);
     this.dataChange.next(this.data);
  }

  changeNode(index:number,name:string)
  {
     this.data[index].item=name;
  }

See that, after change the array -adding a node/subnode we need call to this.dataChange.next(this.data), because a splice change the "content" of the array, but not the array itselft. We can also do some "bizarro" as

 this.data=[...this.data]

but the guys of Angular Material make us our live easy and we can use .next (It's the reason because data really ist not an array else the value of a BehaviorSubject)

I hope this aclaration remove the "magic" of the dinamic-tree. I forked the stackblitz to add tree simple buttons to add a node, add a subnode and change the node

🌐
MUI X
mui.com › x › react-data-grid › tree-data
Data Grid - Tree data - MUI X
See Server-side data—Tree data for details on lazy-loading tree data children.
🌐
GitHub
github.com › mui › material-ui › issues › 17700
[TreeView] Locks up preventing mouse & up/down arrows from working · Issue #17700 · mui/material-ui
October 4, 2019 - TreeView will often lock up, preventing mouse up/down arrows from working. This happens after it has re-rendered, even if the tree data hasn't changed. The issue is present in the latest releas...
Author   oisinlavery
🌐
Enisn-projects
enisn-projects.io › docs › en › uranium › latest › themes › material › components › TreeView
TreeView - Uranium UI - enisn-projects
A file system is a good example of lazy-loading. When you open a folder, you don't want to load all of its children. You want to load children only when the folder is expanded. You can use LoadChildrenCommand to load children from the database when the folder is expanded. public class ...
🌐
MUI X
mui.com › x › react-data-grid › server-side-data › tree-data
Data Grid - Server-side tree data - MUI X
To be able to dynamically load tree data from the server, including lazy-loading of children, you must create a Data Source and pass the dataSource prop to the Data Grid, as detailed in the Server-side data overview.
🌐
GitHub
github.com › mui › material-ui › issues › 35947
MUI TreeView rendering issues · Issue #35947 · mui/material-ui
January 25, 2023 - Hello Team, To display our results, we are using the most recent mui versions and the TreeView component. However, due to the large amount of nested data, MUI TreeView components become stuck durin...
Author   smo043
🌐
Reddit
reddit.com › r/reactjs › performant treeview with lazy loading problem
r/reactjs on Reddit: Performant TreeView with lazy loading problem
May 31, 2024 -

Hello! I have a super specific problem I’d like to consult with you about.

I need to create a Tree View component, for file explorer. But with the following requirements:

  1. Lazy Loading - the children of each node are created dynamically. For example, when pressing a specific directory, a query to retrieve the directory’s children is dispatched - and then I need to render the children. So, I cannot provide the whole tree beforehand.

  2. Performance - should handle large datasets and very deep file systems.

I couldn’t find a good enough library, or perhaps I’m dealing with this task incorrectly.

  • MUI’s simple tree view is not virtualized, and crashes the browser when provided with large datasets.

  • react-arborist is great as it is virtual and performant, but - it renders the tree using a “data” props passed to the parent component. The structure is that each node has children, which are an array of nodes. Here is the problem.

Let’s say the current tree is saved in a state.

Given a very deep directory, when I dispatch a query to retrieve its children - I can’t think of a good enough way to set this state. To understand to which deep node I have to append the children, I’ll have to search it across the whole tree (As the children are arrays). Which makes me a little worried about the performance.

What do you think? Which solution will be good? Potentially I can save the state in a comfortable structure. Let’s say where each node’s children are another object (where each key is the node’s name). Which will make access to deep nodes much better. And then convert this whole structure to the one react-arborist supports (where children are an array). Yet it seems too risky as well.

Thank you!