[TreeView] Async loading support
Fetching Async TreeView Data - javascript
[TreeView] The node takes time to open
How to set expanded prop for material ui TreeView component conditionally
» npm install react-lazy-paginated-tree
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.
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])
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:
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.
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!