DEV Community

Cover image for Building a Simple Tree View Component in React
TD!
TD!

Posted on

Building a Simple Tree View Component in React

Day 11 of #100DaysOfMiva Coding Challenge

Creating intuitive and interactive user interfaces is a fundamental aspect of web development. Today, I worked on building a simple Tree View component using React. This component allows users to navigate through hierarchical data structures seamlessly.

In this article, I will break down the codebase step-by-step, explaining the concepts and technicalities involved. Whether you're a beginner or looking to reinforce your React skills, this walkthrough will guide you through creating your own Tree View component.

Table of Contents

Introduction to Tree View

A Tree View is a graphical user interface component that displays a hierarchical list of items, allowing users to expand and collapse branches to reveal nested items. It's commonly used to represent file systems, organizational structures, and nested menus.

Key Features of a Tree View:

  • Hierarchy Representation: Displays data in a parent-child relationship.
  • Expand/Collapse Functionality: Allows users to view or hide nested items.
  • Interactive Navigation: Users can traverse through different levels seamlessly.

Use Cases:

  • File explorers (e.g., Windows Explorer)
  • Navigation menus in websites and applications
  • Organizational charts
  • Category listings in e-commerce sites

Project Structure

Our project consists of the following files:

css

src/

├── data.js
├── index.jsx
├── menu-item.jsx
├── menu-list.jsx
└── styles.css
Enter fullscreen mode Exit fullscreen mode

Description of Files:

data.js: Contains the hierarchical data structure that will be displayed in the Tree View.
index.jsx: The main component that initiates the Tree View rendering.
menu-list.jsx: Responsible for rendering a list of menu items.
menu-item.jsx: Handles individual menu items, including expand/collapse functionality.
styles.css: Contains the styling for the Tree View components.

Setting Up the Data

We'll start by defining the hierarchical data structure that our Tree View will display.

data.js


// data.js

export const menus = [
  {
    label: "Home",
    to: "/",
  },
  {
    label: "Profile",
    to: "/profile",
    children: [
      {
        label: "Details",
        to: "/profile/details",
        children: [
          {
            label: "Location",
            to: "/profile/details/location",
            children: [
              {
                label: "City",
                to: "/profile/details/location/city",
              },
            ],
          },
        ],
      },
    ],
  },
  {
    label: "Settings",
    to: "/settings",
    children: [
      {
        label: "Account",
        to: "/settings/account",
      },
      {
        label: "Security",
        to: "/settings/security",
        children: [
          {
            label: "Login",
            to: "/settings/security/login",
          },
          {
            label: "Register",
            to: "/settings/security/register",
            children: [
              {
                label: "Random Data",
                to: "/settings/security/register/random-data",
              },
            ],
          },
        ],
      },
    ],
  },
];

export default menus;
Enter fullscreen mode Exit fullscreen mode

Explanation:

  • The menus array contains objects representing each menu item.
  • Each menu item has:
    • label: The text displayed to the user.
    • to: The URL or path the menu item points to.
    • children: An optional array of sub-menu items, allowing for nested structures.
  • This recursive structure allows us to represent complex hierarchies easily.

Example Structure Visualization:


- Home
- Profile
  - Details
    - Location
      - City
- Settings
  - Account
  - Security
    - Login
    - Register
      - Random Data
Enter fullscreen mode Exit fullscreen mode

Creating the Tree View Components

Now, let's build the React components that will render this data as an interactive Tree View.

Prerequisites

Ensure you have a React application set up. You can create one using Create React App:


npx create-react-app tree-view-app
Enter fullscreen mode Exit fullscreen mode

Install necessary dependencies:


npm install react-icons
Enter fullscreen mode Exit fullscreen mode

We will use react-icons for the expand and collapse icons.

TreeView Component (`index.jsx`)

This is the entry point of our Tree View component.


// index.jsx

import React from "react";
import MenuList from "./menu-list";
import menus from "./data";
import "./styles.css";

export default function TreeView() {
  return (
    <div className="tree-view-container">
      <MenuList list={menus} />
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Explanation:

  • Imports:
    • React: To use React functionalities.
    • MenuList: The component responsible for rendering a list of menu items.
    • menus: The data we defined earlier.
    • styles.css: The stylesheet for styling our components.
  • TreeView Function:
    • Returns a div with the class tree-view-container which wraps the MenuList component.
    • Passes the menus data as a prop to MenuList.

Purpose:

  • Acts as a container and initiates the rendering process by supplying the initial data to MenuList.

MenuList Component (menu-list.jsx)

This component renders a list of menu items.


// menu-list.jsx

import React from "react";
import MenuItem from "./menu-item";

export default function MenuList({ list = [] }) {
  return (
    <ul className="menu-list-container">
      {list.map((listItem, index) => (
        <MenuItem key={index} item={listItem} />
      ))}
    </ul>
  );
}
Enter fullscreen mode Exit fullscreen mode

Explanation:

  • Imports:
    • React: For React functionalities.
    • MenuItem: Component responsible for rendering individual menu items.
  • MenuList Function:
    • Accepts a prop list, which is an array of menu items.
    • Returns an unordered list (ul) with the class menu-list-container.
    • Maps over the list array and renders a MenuItem for each item.
    • Uses the index as a key for each MenuItem. In production, it's better to use a unique identifier.

Purpose:

  • Handles rendering a collection of menu items and delegates the rendering of each item to the MenuItem component.
  • Supports recursive rendering by allowing MenuItem components to render nested MenuLists.

MenuItem Component (menu-item.jsx)

This component renders individual menu items and handles expand/collapse functionality for items with children.


// menu-item.jsx

import React, { useState } from "react";
import MenuList from "./menu-list";
import { FaMinus, FaPlus } from "react-icons/fa";

export default function MenuItem({ item }) {
  const [isExpanded, setIsExpanded] = useState(false);

  const hasChildren = item.children && item.children.length > 0;

  const handleToggle = () => {
    setIsExpanded((prev) => !prev);
  };

  return (
    <li>
      <div className="menu-item">
        <p>{item.label}</p>
        {hasChildren && (
          <span onClick={handleToggle} className="toggle-icon">
            {isExpanded ? <FaMinus /> : <FaPlus />}
          </span>
        )}
      </div>
      {hasChildren && isExpanded && <MenuList list={item.children} />}
    </li>
  );
}
Enter fullscreen mode Exit fullscreen mode

Explanation:

  • Imports:
    • React and useState: For React functionalities and managing component state.
    • MenuList: To render nested lists if the item has children.
    • FaMinus and FaPlus: Icons from react-icons for expand/collapse indicators.
  • MenuItem Function:
    • isExpanded State: Manages whether the item's children are visible.
      • Initialized to false (collapsed).
      • Toggled by the handleToggle function.
    • hasChildren Variable:
      • Checks if the current item has children.
    • handleToggle Function:
      • Toggles the isExpanded state between true and false.
    • Rendering:
      • <li> Element: Wraps each menu item.
      • <div className="menu-item">:
        • Contains the item's label and the toggle icon if the item has children.
        • <p>{item.label}</p>: Displays the item's label.
        • Toggle Icon:
          • Rendered only if the item has children.
          • Clicking the icon calls handleToggle.
          • Displays FaMinus if expanded, FaPlus if collapsed.
      • Nested MenuList:
        • Rendered only if the item has children and isExpanded is true.
        • Passes item.children as the list prop to render nested items.

Purpose:

  • Display Menu Item: Shows the label and, if applicable, the expand/collapse icon.
  • Manage Expand/Collapse State: Uses useState to control the visibility of child items.
  • Recursive Rendering: Supports nested structures by rendering MenuList within itself for child items.

Concepts Demonstrated:

  • State Management: Using useState to manage UI state.
  • Conditional Rendering: Rendering elements based on conditions (e.g., whether an item has children, whether it's expanded).
  • Event Handling: Toggling state in response to user interactions (click events).
  • Recursion in Components: Rendering components within themselves to handle nested data structures.

Styling the Components (styles.css)

Proper styling enhances the user experience by making the interface intuitive and visually appealing.


/* styles.css */

.tree-view-container {
  min-height: 100vh;
  width: 300px;
  background-color: #00476e;
  padding: 20px;
  color: #ffffff;
  font-family: Arial, sans-serif;
}

.menu-list-container {
  list-style: none;
  padding-left: 20px;
  margin: 5px 0;
}

.menu-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  cursor: pointer;
  padding: 5px 0;
}

.menu-item p {
  margin: 0;
}

.toggle-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  background-color: #0078a8;
  border-radius: 50%;
  color: #ffffff;
  font-size: 14px;
}

.toggle-icon:hover {
  background-color: #005f82;
}
Enter fullscreen mode Exit fullscreen mode

Explanation:

  • .tree-view-container:
    • Sets the height, width, background color, padding, text color, and font for the Tree View container.
  • .menu-list-container:
    • Removes default list styling and adds indentation for nested lists.
  • .menu-item:
    • Arranges the label and toggle icon horizontally.
    • Adds spacing and cursor styling for better UX.
  • .menu-item p:
    • Removes default margin for proper alignment.
  • .toggle-icon:
    • Styles the expand/collapse icons with background color, size, and hover effects to make them easily identifiable and interactive.

Purpose:

  • Provides a clean and consistent look to the Tree View component.
  • Enhances usability by making interactive elements visually distinct.
  • Maintains readability and organization across different levels of the hierarchy.

Putting It All Together

Now, let's see how all these components work together to render the Tree View.

App Entry Point (App.js):


// App.js

import React from "react";
import TreeView from "./TreeView";

function App() {
  return (
    <div className="App">
      <TreeView />
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

Directory Structure:


src/

├── data.js
├── TreeView/
   ├── index.jsx
   ├── menu-item.jsx
   ├── menu-list.jsx
   └── styles.css
└── App.js
Enter fullscreen mode Exit fullscreen mode

Running the Application:

Start the Development Server:


npm start
Enter fullscreen mode Exit fullscreen mode

View in Browser:

Navigate to http://localhost:3000/ to see the Tree View in action.

Interactive Features:

Expanding and Collapsing:

Click on the plus (+) icon to expand a menu item and reveal its children.
Click on the minus (-) icon to collapse and hide the children.

Nested Levels:

The component supports infinite nesting levels, as demonstrated by the deeply nested "City" and "Random Data" items.

Styling Consistency:

The styling ensures a consistent look and feel across all levels, with proper indentation and visual cues.

Conclusion

Building a Tree View component in React involves understanding and implementing several key concepts:

Recursive Components: Leveraging recursion to handle nested data structures elegantly.

State Management: Using React's useState hook to manage UI state, such as the expand/collapse functionality.

Conditional Rendering: Rendering elements based on certain conditions, enhancing interactivity.

Modular Design: Breaking down the UI into reusable and maintainable components.

Styling: Applying consistent and responsive styles to improve user experience.

This project demonstrates how to construct a functional and interactive Tree View from scratch, providing a solid foundation for more complex implementations. Whether for navigation menus, file explorers, or organizational charts, understanding these principles enables developers to create versatile and dynamic interfaces.

Possible Improvements

While the current implementation serves as a solid starting point, there are several enhancements that can be made:

Routing Integration: Utilize React Router to navigate to different pages when clicking on menu items with the to property.

Dynamic Data Loading: Implement lazy loading for children, fetching data from an API when a parent item is expanded.

Accessibility Enhancements: Add keyboard navigation support and ARIA attributes for better accessibility.

Animation Effects: Incorporate smooth transition animations when expanding and collapsing menu items.

Search Functionality: Add a search bar to filter and highlight menu items dynamically.

Customization Options: Allow customization of styles and icons through props for greater flexibility.

Implementing these improvements would not only enrich the user experience but also make the component more robust and adaptable to various use cases.

Thank you for following along this walkthrough! I hope this explanation provides clarity and insight into building recursive, interactive components in React. Happy coding!

GitHub Repository: Day 11 of #100DaysOfMiva on GitHub

Feel free to check out the repository for the complete code and follow my progress throughout the #100DaysOfMiva coding challenge.

Top comments (0)