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.
Encapsulation: The compound component pattern encapsulates related functionality within a parent component and its children.
Flexibility: Users can use the subcomponents in any order or combination that makes sense for their specific use case.
Communication: Subcomponents can communicate with each other through the parent component, often using React's context API to manage shared state.
Let's create a simple Accordion
component as a compound component.
Accordion: The parent component that holds the state and context.
AccordionItem: A child component that represents a single item in the accordion.
AccordionHeader: A child component for the header of an item.
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;
Accordion: Manages the state (openIndex
) and provides context to its children.
AccordionItem: Displays its children if it is the currently open item.
AccordionHeader: Toggles the open state of the item when clicked.
AccordionContent: Displays the content of an item.
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.