4 min read react

Dropdowns in React: Unveiling the Compound Component Pattern

A deep dive into creating a feature-rich React dropdown menu, enhanced with keyboard navigation and the Compound Component Pattern for improved accessibility and maintainability.

Crafting an Advanced Dropdown in React: A Complete Guide Using the Compound Component Pattern

Let’s create an advanced dropdown menu in React, complete with keyboard navigation and accessibility features, using the Compound Component Pattern. This example will demonstrate how to build a cohesive and interactive dropdown component.

Step 1: Building the Dropdown Component

We begin by setting up the Dropdown component with shared state and context.

import React, { useState, createContext, useContext } from "react";

const DropdownContext = createContext();

const Dropdown = ({ children, onSelect }) => {
  const [isOpen, setIsOpen] = useState(false);
  const [highlightedIndex, setHighlightedIndex] = useState(-1);
  const [items, setItems] = useState(["Option 1", "Option 2", "Option 3"]);

  const toggle = () => setIsOpen(!isOpen);
  const close = () => setIsOpen(false);
  const handleSelect = (item) => {
    onSelect(item);
    close();
  };

  const contextValue = {
    isOpen,
    toggle,
    highlightedIndex,
    setHighlightedIndex,
    items,
    handleSelect,
  };

  return (
    <DropdownContext.Provider value={contextValue}>
      <div className="dropdown">{children}</div>
    </DropdownContext.Provider>
  );
};

export default Dropdown;

Step 2: Implementing the Toggle Component

Create the Toggle component to control the dropdown’s visibility.

Dropdown.Toggle = () => {
  const { isOpen, toggle } = useContext(DropdownContext);
  return (
    <button onClick={toggle} aria-haspopup="true" aria-expanded={isOpen}>
      Toggle Dropdown
    </button>
  );
};

Step 3: Creating the List and Item Components

Develop the List and Item components to display the dropdown options.

Dropdown.List = () => {
  const { isOpen, highlightedIndex, items } = useContext(DropdownContext);
  return isOpen ? (
    <ul className="dropdown-list">
      {items.map((item, index) => (
        <Dropdown.Item
          key={index}
          item={item}
          index={index}
          isHighlighted={index === highlightedIndex}
        />
      ))}
    </ul>
  ) : null;
};

Dropdown.Item = ({ item, index, isHighlighted }) => {
  const { handleSelect } = useContext(DropdownContext);
  return (
    <li
      className={`dropdown-item ${isHighlighted ? "highlighted" : ""}`}
      onClick={() => handleSelect(item)}
    >
      {item}
    </li>
  );
};

Step 4: Adding Keyboard Navigation

Enhance the Dropdown component to handle keyboard interactions.

// Inside Dropdown component, add the following methods:

const highlightNext = () => {
  setHighlightedIndex((prev) => (prev < items.length - 1 ? prev + 1 : prev));
};

const highlightPrev = () => {
  setHighlightedIndex((prev) => (prev > 0 ? prev - 1 : prev));
};

const handleKeyDown = (e) => {
  switch (e.key) {
    case "ArrowDown":
      e.preventDefault();
      highlightNext();
      break;
    case "ArrowUp":
      e.preventDefault();
      highlightPrev();
      break;
    case "Enter":
      e.preventDefault();
      handleSelect(items[highlightedIndex]);
      break;
    case "Escape":
      close();
      break;
    default:
      break;
  }
};

// Update the return statement to include onKeyDown handler
return (
  <DropdownContext.Provider value={contextValue}>
    <div className="dropdown" onKeyDown={handleKeyDown} tabIndex="0">
      {children}
    </div>
  </DropdownContext.Provider>
);

Step 5: Integrating the Dropdown in an Application

Here’s the updated App component which includes the items for the dropdown:

const App = () => {
  const handleSelect = (item) => {
    console.log("Selected Item:", item);
  };

  return (
    <Dropdown onSelect={handleSelect}>
      <Dropdown.Toggle />
      <Dropdown.List>
        <Dropdown.Item item="Item 1">Item 1</Dropdown.Item>
        <Dropdown.Item item="Item 2">Item 2</Dropdown.Item>
        <Dropdown.Item item="Item 3">Item 3</Dropdown.Item>
      </Dropdown.List>
    </Dropdown>
  );
};

export default App;

In this implementation, each Dropdown.Item is explicitly declared within the Dropdown.List. This ensures that the dropdown menu displays “Item 1”, “Item 2”, and “Item 3” as options, maintaining the structure and functionality of our advanced dropdown component.

Conclusion

This comprehensive example demonstrates building a feature-rich dropdown in React using the Compound Component Pattern. The dropdown component is not only interactive but also accessible, thanks to keyboard navigation. This pattern offers a scalable and maintainable approach to developing complex UI components.

Fun Fact: Did you know that dropdown menus in web design were initially inspired by similar concepts in desktop operating systems? They were one of the early UI elements that made the leap from desktop to web, adapting to the new environment like a chameleon!

Joke of the Day: Why did the JavaScript developer leave the restaurant? Because they couldn’t find the menu! 😄

Remember, in React just like in a good restaurant, a well-organized and accessible menu (or dropdown) can make a world of difference in user experience. Keep coding and keep smiling!

Read Next

Post image for Unveiling INP: The Key to Understanding Web Interactivity
This article discusses the Interaction to Next Paint (INP) metric, highlighting its role in assessing web interactivity and responsiveness, and offers strategies for optimization.
Post image for Advanced Component Composition in React
Explore the art of component composition in React, showcasing how it can elegantly handle complex scenarios like theme management in a project management app's interactive dashboard.