Search code examples
reactjsmaterial-uitreefrontendtreeview

Mui v5 - TreeView - On parent collapse, collapse all descendant


When I collapse a TreeItem, I want all it's descendants TreeItems (it's children, their children, etc.) that are expanded to also collapse. How can I do that? Visual flow:

1.Initial state:

TreeItem #1

2.TreeItem #1 Expanded:

TreeItem #1
  TreeItem #2

3.TreeItem #2 Expanded:

TreeItem #1
  TreeItem #2
    TreeItem #3

4.TreeItem #1 Collapsed:

TreeItem #1

5.TreeItem #1 Expanded for the second time - WANTED CASE:

TreeItem #1
  TreeItem #2

5.TreeItem #1 Expanded for the second time - WHAT ACTUALLY HAPPENS:

TreeItem #1
  TreeItem #2
    TreeItem #3

Solution

  • To accomplish this, you need to override the default behavior of the TreeView component. This isn't extremely difficult, but can be somewhat confusing because the documentation isn't great.

    You will have to make the expanded property of the TreeView controlled. Basically, this means having a state control which nodes are expanded/collapsed.

    There are multiple ways to interact with the node, there are onClick methods, such as the onNodeSelect, onNodeToggle, and etc., you will need a way of capturing when the user clicks-to-expand.

    Once you have the above, you can just have a function modify the state like so:

    import { TreeItem, TreeView } from "@mui/lab";
    
    function TreeDiagram(): JSX.Element {
       const [expanded, setExpanded] = React.useState<string[]>([]);
       
    
       /**
        * Method to remove or include a node to the array of expanded node
        */
       const handleExpandedToggle = (nodeId: string): void => {
          setExpanded((prevExpandedState: string[]): string[] => 
             prevExpandedState.includes(nodeId)
                ? prevExpandedState.filter((node: string) => node !== nodeId)
                : [nodeId, ...prevExpandedState]);
       };
    
       /**
        * Method to handle the click event of the TreeItem's
        */
       const handleTreeItemClick = (event: React.SyntheticEvent, nodeId: string): void => {
          handleExpandedToggle(nodeId);
       };
    
       return (
          <TreeView
             expanded={expanded}
          >
             {/* All the tree items */}
             <TreeItem
                onNodeFocus={handleTreeItemClick}
                nodeId={"tree_item_1"}
                label={ /* Whatever you'd like here */ }
             ></TreeItem>
          </TreeView>
       );
    };