DEV Community

Cover image for Mastering Dynamic Menu Implementation in React: Advanced State Management
Gleidson Leite da Silva
Gleidson Leite da Silva

Posted on

Mastering Dynamic Menu Implementation in React: Advanced State Management

Discover how to implement a professional-grade dynamic menu in React that your users will love

The Art of Creating Perfect Dynamic Menus

Imagine a critical hospital system where doctors constantly switch between different sections during an emergency. Every second counts, and the last thing they need is to waste time navigating through a confusing menu or having to reopen multiple sections every time they refresh the page. This is where our revolutionary solution comes into play.

In this article, you'll discover how to create a dynamic menu in React that not only maintains its state perfectly between sessions but does so without relying on LocalStorage - an approach that elegantly combines security and performance.

The Real Challenge

In modern enterprise applications, especially in critical environments like hospital systems, banking, or real-time systems, user experience cannot be compromised. Some specific challenges include:

  • Maintaining consistent navigation states between refreshes
  • Ensuring performance even with complex menu structures
  • Implementing a solution that scales with application growth
  • Maintaining security without relying on local storage

The Solution: Intelligent Data Structure

Our approach utilizes a tree data structure combined with efficient search algorithms. Let's dive into the technical details that make this possible.

Menu Tree Architecture

The foundation of our implementation is a carefully designed tree data structure. Each node represents a menu item that can contain submenus, creating a natural and intuitive hierarchy.

export enum MenuType {
  PRIMARY = 'PRIMARY',
  SECONDARY = 'SECONDARY',
  TERTIARY = 'TERTIARY'
}

export interface TreeNode {
  key: string;
  label: string;
  icon?: ReactNode;
  path?: string;
  children?: TreeNode[];
  type: MenuType;
}

export const menuTree: TreeNode[] = [
  {
    key: "/",
    label: "Dashboard",
    icon: <PieChartOutlined />,
    path: "/",
    type: MenuType.PRIMARY,
  },
  {
    key: "/patients",
    label: "Patient Management",
    icon: <TeamOutlined />,
    type: MenuType.PRIMARY,
    children: [
      {
        key: "/patients/list",
        label: "Patient List",
        path: "/patients/list",
        type: MenuType.SECONDARY,
      },
      {
        key: "/patients/admissions",
        label: "Admissions",
        path: "/patients/admissions",
        type: MenuType.SECONDARY,
      }
    ]
  },
  // Additional menu items...
];
Enter fullscreen mode Exit fullscreen mode

Efficient Indexing System

One of the keys to our solution's exceptional performance is the indexing system:

export const createMenuIndex = (tree: TreeNode[]): Map<string, TreeNode> => {
  const index = new Map<string, TreeNode>();
  const traverse = (nodes: TreeNode[]) => {
    nodes.forEach(node => {
      if (node.path) {
        index.set(node.path, node);
      }
      if (node.children) {
        traverse(node.children);
      }
    });
  };
  traverse(tree);
  return index;
};
Enter fullscreen mode Exit fullscreen mode

Intelligent Navigation Management

The true differentiator of our implementation lies in how we manage navigation and menu state:

export function findParentKeys({ 
  tree, 
  targetKey, 
  parentKeys = [] 
}: {
  tree: TreeNode[];
  targetKey: string;
  parentKeys?: string[];
}): string[] {
  for (const node of tree) {
    if (node.key === targetKey) {
      return parentKeys;
    }
    if (node.children) {
      const found = findParentKeys({
        tree: node.children,
        targetKey,
        parentKeys: [...parentKeys, node.key]
      });
      if (found.length) return found;
    }
  }
  return [];
}
Enter fullscreen mode Exit fullscreen mode

Optimized React Component

The final component elegantly integrates all these functionalities:

import React, { useState, useEffect, useMemo } from 'react';
import { Layout, Menu } from 'antd';
import { useNavigate, useLocation } from 'react-router-dom';
import type { MenuProps } from 'antd';
import { PieChartOutlined, MenuFoldOutlined, MenuUnfoldOutlined } from '@ant-design/icons';

const { Sider } = Layout;
type MenuClickEventHandler = MenuProps['onClick'];

const MenuComponent: React.FC = () => {
  const [currentMenu, setCurrentMenu] = useState<MenuType>(MenuType.PRIMARY);
  const [currentSelectedMenuKey, setCurrentSelectedMenuKey] = useState('');
  const [isMenuCollapsed, setIsMenuCollapsed] = useState(false);
  const [openKeys, setOpenKeys] = useState<string[]>([]);

  const navigate = useNavigate();
  const location = useLocation();
  const menuIndex = useMemo(() => createMenuIndex(menuTree), []);

  useEffect(() => {
    const foundNode = menuIndex.get(location.pathname);
    if (foundNode) {
      setCurrentMenu(foundNode.type);
      setCurrentSelectedMenuKey(foundNode.key);
      const parentKeys = findParentKeys({ 
        targetKey: foundNode.key, 
        tree: menuTree 
      });
      setOpenKeys(previous => Array.from(new Set([...previous, ...parentKeys])));
    }
  }, [location.pathname, menuIndex]);

  const handleOnMenuItemClick: MenuClickEventHandler = ({ key }) => {
    const foundNode = menuIndex.get(key);
    if (foundNode?.path) {
      navigate(foundNode.path);
    }
  };

  const toggleCollapsed = () => {
    setIsMenuCollapsed(!isMenuCollapsed);
  };

  const menuItems = useMemo(() => {
    const convertTreeToAntdItems = (nodes: TreeNode[]): MenuProps['items'] =>
      nodes.map(node => ({
        key: node.path || node.key,
        icon: node.icon,
        label: node.label,
        children: node.children ? convertTreeToAntdItems(node.children) : undefined,
      }));

    return convertTreeToAntdItems(menuTree);
  }, []);

  return (
    <Sider 
      width={isMenuCollapsed ? 100 : 280} 
      style={{ 
        boxShadow: '2px 4px 6px -1px rgba(0, 0, 0, 0.1)',
        minHeight: '100vh'
      }}
    >
      <Menu
        onClick={handleOnMenuItemClick}
        items={menuItems}
        mode="inline"
        selectedKeys={[currentSelectedMenuKey]}
        inlineCollapsed={isMenuCollapsed}
        openKeys={openKeys}
        onOpenChange={setOpenKeys}
        style={{ height: '100%', borderRight: 0 }}
      />
      <div 
        onClick={toggleCollapsed} 
        style={{ 
          padding: '16px',
          textAlign: 'center',
          cursor: 'pointer',
          borderTop: '1px solid #f0f0f0'
        }}
      >
        {isMenuCollapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
      </div>
    </Sider>
  );
};

export default MenuComponent;
Enter fullscreen mode Exit fullscreen mode

Results and Benefits

Our implementation brings tangible benefits:

  1. Exceptional Performance: Instant navigation even in complex menus
  2. Enhanced Security: No dependency on local storage
  3. Superior User Experience: Persistent states without compromises
  4. Maintainability: Clean and well-structured code
  5. Scalability: Ready to grow with your application

Implementation Steps

To implement this solution in your project:

  1. Install the required dependencies:
npm install antd @ant-design/icons react-router-dom
Enter fullscreen mode Exit fullscreen mode
  1. Copy the menu component and supporting functions
  2. Set up your routing structure
  3. Configure your menu tree structure
  4. Integrate the component into your layout
  5. Customize the styling to match your brand

Beyond the Basics

To take this implementation further, consider:

  • Adding keyboard navigation support
  • Implementing menu search functionality
  • Adding animation for state transitions
  • Creating a context for global menu state management
  • Adding accessibility features (ARIA labels, keyboard shortcuts)

Conclusion

Implementing dynamic menus in React doesn't have to be challenging. With the approach presented, you have a robust, performant, and secure solution that will elevate your application's quality to a new level.

Try this implementation in your next project and discover why leading enterprises are adopting this approach for their critical systems.

Top comments (0)