Compound Components Pattern
The Compound Components pattern allows you to create expressive and flexible component APIs where multiple components work together to accomplish a task.
What are Compound Components?
Compound components are a pattern where components are used together such that they share an implicit state. Think of HTML elements like <select> and <option> - they work together!
Example Implementation
// Toggle compound component
const ToggleContext = React.createContext();
function Toggle({ children }) {
const [on, setOn] = React.useState(false);
const toggle = () => setOn(!on);
return (
<ToggleContext.Provider value={{ on, toggle }}>
{children}
</ToggleContext.Provider>
);
}
function ToggleOn({ children }) {
const { on } = React.useContext(ToggleContext);
return on ? children : null;
}
function ToggleOff({ children }) {
const { on } = React.useContext(ToggleContext);
return on ? null : children;
}
function ToggleButton(props) {
const { on, toggle } = React.useContext(ToggleContext);
return <button onClick={toggle} {...props} />;
}
// Usage
<Toggle>
<ToggleOn>The button is on</ToggleOn>
<ToggleOff>The button is off</ToggleOff>
<ToggleButton>Toggle</ToggleButton>
</Toggle>
Benefits
- Flexible Markup Structure: Users can rearrange components
- Reduced Prop Drilling: State is shared via context
- Clear Parent-Child Relationship: API is intuitive
- Separation of Concerns: Each component has a single responsibility
Real-World Examples
- Reach UI’s Accordion
- Material-UI’s Stepper
- Ant Design’s Form components
Key Takeaways
- Use React Context to share state between compound components
- Provide static properties for better developer experience
- Consider adding validation for required child components
- Document the relationship between components clearly