| 2023-07-24

Creating a Collapsible Tree View in React

    Introduction:

    In this tutorial, we will learn how to convert a flat data structure into a hierarchical tree view with collapsible nodes using React. This can be useful when displaying nested data, such as a category hierarchy or a folder structure.

    Example app deployed at Vercel.

    Data Structure:

      // This is your initial flat data structure
      const data: Item[] = [
        { name: "Electronics", id: "1", parent: null },
        { name: "Furniture", id: "2", parent: null },
        { name: "Smartphones", id: "3", parent: "1" },
        { name: "Laptops", id: "4", parent: "1" },
        { name: "Chairs", id: "5", parent: "2" },
        { name: "Gaming Laptops", id: "6", parent: "4" },
        { name: "iPhone", id: "7", parent: "3" },
        { name: "MacBook Pro", id: "8", parent: "4" },
        { name: "Office Chairs", id: "9", parent: "5" },
        { name: "Dining Chairs", id: "10", parent: "5" },
        { name: "Recliners", id: "11", parent: "5" },
        { name: "Gaming Chairs", id: "12", parent: "5" }, // Corrected placement
      ];
    
    JavaScript

    Prerequisites:

    Basic knowledge of React and JavaScript.

    Step 1: Set Up the Project

    Create a new React project using Create React App or your preferred method.

    Step 2: Define the Data Structure

    Define the structure of each item in your data. For example, each item might have a name, unique ID, and a reference to its parent item (if it's a child item).

    // Define the structure of an item in your data
    type Item = {
      name: string;
      id: string;
      parent: string | null;
    };
    
    JavaScript

    Step 3: Build the Nested Structure

    Create a function that converts the flat data structure into a hierarchical nested structure. We'll call this function buildNestedStructure. It will iterate through the flat data and construct a map of items with their children.

    type NestedItem = Item & { children: NestedItem[] };
    // This function converts an array of Items into a hierarchical structure of NestedItems
    const buildNestedStructure = (items: Item[]): NestedItem[] => {
      const itemMap: { [id: string]: NestedItem } = {};
    
      // Function to create a NestedItem from an Item
      const createNestedItem = (item: Item): NestedItem => ({
        ...item,
        children: [],
      });
    
      // Loop through each item in the provided items array
      items.forEach((item) => {
        // For each item, create a new NestedItem by calling the createNestedItem function,
        // and add it to the itemMap object with its ID as the key.
        // This will create a new object that contains all the properties of the original item,
        // plus an empty children array.
        // It's worth noting that at this point, all NestedItems in the itemMap have an empty children array.
        itemMap[item.id] = createNestedItem(item);
        // Next, check if the current item has a parent.
        // If it does, it means it should be nested inside another item.
        if (item.parent) {
          // If the item has a parent, add it to the children array of its parent NestedItem in the itemMap.
          // This is where the hierarchical nesting happens.
          // Note that since we're directly modifying the itemMap objects,
          // the same changes will be reflected in all places where these objects are referenced.
          // So when we added this item to its parent's children array,
          // it also appeared in the children array of the same item in the itemMap.
          itemMap[item.parent].children.push(itemMap[item.id]);
        }
      });
    
      // Return the array of top-level items, each of which has its children nested within it
      return items.filter((item) => !item.parent).map((item) => itemMap[item.id]);
    };
    
    JavaScript

    Step 4: Create the Recursive Component

    Create a React component to render the nested structure. This component will be recursive, meaning it will render itself for each child item. We'll call this component RenderItem.

    // This component takes a NestedItem and a depth, and renders the item and its children
    const RenderItem: React.FC<{ item: NestedItem; depth: number }> = ({
      item,
      depth,
    }) => {
      const [isExpanded, setIsExpanded] = useState(true);
    
      const handleToggle = () => {
        setIsExpanded(!isExpanded);
      };
    
      const paddingStyle = { paddingLeft: `${depth * 20}px` };
    
      return (
        <div style={paddingStyle}>
          {hasChildren && (
            <button onClick={handleToggle}>
              {isExpanded ? "▼" : "►"} {item.name}
            </button>
          )}
          {isExpanded &&
            item.children.map((child) => (
              <RenderItem key={child.id} item={child} depth={depth + 1} />
            ))}
        </div>
      );
    };
    
    JavaScript

    Step 5: Implement Collapsible Functionality

    Add the ability to collapse and expand nodes in the tree view. We'll use React's useState hook to track the expanded state of each node. When the user clicks on a node with children, it will toggle between expanded and collapsed states.

    // Check if the item has children or not
      const hasChildren = item.children.length > 0;
    
      return (
        <div style={paddingStyle}>
          {hasChildren ? (
            <button onClick={handleToggle}>
              {isExpanded ? "▼" : "►"} {item.name}
            </button>
          ) : (
            <div>{item.name}</div>
          )}
          {isExpanded &&
            item.children.map((child) => (
              <RenderItem key={child.id} item={child} depth={depth + 1} />
            ))}
        </div>
      );
    
    JavaScript

    Step 6: Render the Tree View

    In the main component of your app, use the buildNestedStructure function to convert your flat data into a nested structure. Then, render the top-level items using the RenderItem component, passing the nested structure as props.

    export default function MainComponent() {
      // This is your initial flat data structure
      const data: Item[] = [
        // ... Your data here ...
      ];
    
      // Build the nested structure from your flat data
      const nestedData = buildNestedStructure(data);
    
      // Render the top-level items, passing a depth of 0
      return (
        <div>
          {nestedData.map((item) => (
            <RenderItem key={item.id} item={item} depth={0} />
          ))}
        </div>
      );
    }
    
    JavaScript

    Conclusion:

    Congratulations! You've successfully created a collapsible tree view from a flat data structure using React. This allows you to display nested data in a user-friendly and organized way, with the ability to expand and collapse nodes as needed.

    Here is the full code for reference.

    Extra Tips:

    • You can further enhance the tree view by adding animations to the expanding and collapsing of nodes.
    • Consider using icons or different styles to visually distinguish between parent and child nodes.
    • Experiment with different ways of structuring your data to best fit your specific use case.

    This tutorial provides a basic example of how to create a collapsible tree view in React. Depending on your project's complexity and requirements, you may need to adjust and customize the implementation accordingly.


    Thanks for reading. If you enjoyed this post, I invite you to explore more of my site. I write about web development, programming, and other fun stuff.