Tenshi
Tenshi

Reputation: 97

Material UI Tree View, check if the node i click has children

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

Answers (1)

Jarrod McGuire
Jarrod McGuire

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

Related Questions