MM
lights

Compound Component In React

#React#TypeScript#webdev

A compound component in React is a design pattern that allows for building complex components out of smaller, simpler components that work together. This pattern is particularly useful when you want to create a flexible and reusable UI component with multiple subcomponents that can be composed together.

Key Features of Compound Components

  1. Encapsulation: The compound component pattern encapsulates related functionality within a parent component and its children.

  2. Flexibility: Users can use the subcomponents in any order or combination that makes sense for their specific use case.

  3. Communication: Subcomponents can communicate with each other through the parent component, often using React's context API to manage shared state.

Example

Let's create a simple Accordion component as a compound component.

  1. Accordion: The parent component that holds the state and context.

  2. AccordionItem: A child component that represents a single item in the accordion.

  3. AccordionHeader: A child component for the header of an item.

  4. AccordionContent: A child component for the content of an item.

import React, { useState, createContext, useContext, ReactNode } from 'react';

interface AccordionContextProps {
  openIndex: number | null;
  toggleIndex: (index: number) => void;
}

const AccordionContext = createContext<AccordionContextProps | undefined>(undefined);

interface AccordionProps {
  children: ReactNode;
}

const Accordion: React.FC<AccordionProps> = ({ children }) => {
  const [openIndex, setOpenIndex] = useState<number | null>(null);

  const toggleIndex = (index: number) => {
    setOpenIndex(openIndex === index ? null : index);
  };

  return (
    <AccordionContext.Provider value={{ openIndex, toggleIndex }}>
      <div>{children}</div>
    </AccordionContext.Provider>
  );
};

interface AccordionItemProps {
  index: number;
  children: ReactNode;
}

const AccordionItem: React.FC<AccordionItemProps> = ({ index, children }) => {
  const context = useContext(AccordionContext);

  if (!context) {
    throw new Error('AccordionItem must be used within an Accordion');
  }

  const { openIndex } = context;
  const isOpen = openIndex === index;

  return <div>{isOpen && children}</div>;
};

interface AccordionHeaderProps {
  index: number;
  children: ReactNode;
}

const AccordionHeader: React.FC<AccordionHeaderProps> = ({ index, children }) => {
  const context = useContext(AccordionContext);

  if (!context) {
    throw new Error('AccordionHeader must be used within an Accordion');
  }

  const { toggleIndex } = context;

  return <h3 onClick={() => toggleIndex(index)}>{children}</h3>;
};

interface AccordionContentProps {
  children: ReactNode;
}

const AccordionContent: React.FC<AccordionContentProps> = ({ children }) => {
  return <div>{children}</div>;
};

// Usage
const App: React.FC = () => (
  <Accordion>
    <AccordionHeader index={0}>Item 1</AccordionHeader>
    <AccordionItem index={0}>
      <AccordionContent>Content 1</AccordionContent>
    </AccordionItem>

    <AccordionHeader index={1}>Item 2</AccordionHeader>
    <AccordionItem index={1}>
      <AccordionContent>Content 2</AccordionContent>
    </AccordionItem>
  </Accordion>
);

export default App;

Explanation

  1. Accordion: Manages the state (openIndex) and provides context to its children.

  2. AccordionItem: Displays its children if it is the currently open item.

  3. AccordionHeader: Toggles the open state of the item when clicked.

  4. AccordionContent: Displays the content of an item.

Benefits

  • Reusability: Subcomponents can be reused and recombined in different ways.

  • Separation of Concerns: Each subcomponent handles a specific part of the overall component's functionality.

  • Readability: The structure of the component is clear and easy to understand.

Compound components are a powerful pattern in React, enabling the creation of complex, flexible, and reusable UI components.