Reputation: 97
so im trying to create a tree using the Tree View from Material UI ( https://material-ui.com/components/tree-view/). We can take the example from their page:
const data = {
id: 'root',
name: 'Parent',
children: [
{
id: '1',
name: 'Child - 1',
},
{
id: '3',
name: 'Child - 3',
children: [
{
id: '4',
name: 'Child - 4',
},
],
},
],
};
const useStyles = makeStyles({
root: {
height: 110,
flexGrow: 1,
maxWidth: 400,
},
});
export default function RecursiveTreeView() {
const classes = useStyles();
const renderTree = (nodes) => (
<TreeItem key={nodes.id} nodeId={nodes.id} label={nodes.name}>
{Array.isArray(nodes.children) ? nodes.children.map((node) => renderTree(node)) : null}
</TreeItem>
);
return (
<TreeView
className={classes.root}
defaultCollapseIcon={<ExpandMoreIcon />}
defaultExpanded={['root']}
defaultExpandIcon={<ChevronRightIcon />}
>
{renderTree(data)}
</TreeView>
);
}
What i want to do, is that whenever i click on a node, to fire some function only if its a leaf node. The "onNodeSelect" props that the treeview has only gives me the node id but not any other information about the node. I know one aproach is to search the original json for that id and find the node and then check if it has childrens, but my json is quite large and i would like not to do that. Any other way?
Upvotes: 1
Views: 4647
Reputation: 683
You could potentially use the onLabelClick of the TreeItem component and ditch the "selection" part of the treeview (e.g. use disableSelection on the TreeView).
Then your code could become
const handleTreeItemClick = (node) => {
if( node.children && node.children.length ) {
// do some stuff
} else {
// do something else
}
}
const renderTree = (nodes) => (
<TreeItem
key={nodes.id}
nodeId={nodes.id}
label={nodes.name}
onLabelClick={() => handleTreeItemClick(nodes)}
>
{Array.isArray(nodes.children) ? nodes.children.map((node) => renderTree(node)) : null}
</TreeItem>
);
Otherwise you might be able to make a hash of the data with ids pointing to the data node (not sure of performance hashing a large json object?), then just lookup the value from the hash rather than searching every time. Annoying I know e.g.
// do this outside of the render, perhaps in a redux selector or reducer
const hashMethod = (nodes, hash = {}) => {
return nodes.reduce((h, node) => {
h[node.id] = node;
if( node.children) {
h = hashMethod(node.children, h);
}
return h;
}, hash);
}
// sorry turned it into an array first so I can use reduce easier
const hashedData = hashMethod([data]);
Then in an onNodeSelect handler within the component (for a single select)
const handleNodeSelect = (nodeId) => {
const node = hashedData(nodeId);
// do what you need with the data
}
Upvotes: 2