Compound Component pattern
React has become the go-to library for building user interfaces, thanks to its component-based architecture. As applications grow in complexity, organizing components in a reusable and maintainable way becomes crucial.
M Zeeshan
8/16/20243 min read
What is the Compound Component Pattern?
The Compound Component Pattern is a design approach where a parent component (often called the "compound component") manages the state and behavior, while child components (the "sub-components") are responsible for rendering specific parts of the UI. The children are typically self-contained but rely on the parent component for context, enabling them to work together cohesively.
This pattern is particularly useful when you have a group of related components that need to share state and behavior, such as a form with multiple fields, a tabbed navigation system, or a dropdown menu.
Why Use the Compound Component Pattern?
Separation of Concerns: By dividing a complex UI into smaller, focused components, each part of the UI is easier to understand, maintain, and test.
Flexibility: The pattern allows for a high degree of customization. The user of the compound component can decide which sub-components to render, in what order, and with what content, without needing to modify the internals of the compound component itself.
Reusability: Sub-components can be reused in different contexts, and the compound component can be reused across different parts of the application.
Declarative API: The pattern promotes a clean, declarative API where the structure of the components reflects the structure of the rendered UI. This leads to more readable and understandable code.
Example: A Custom Toggle Component
Let’s build a simple example to illustrate the Compound Component Pattern. Suppose we want to create a custom toggle component with an on/off state. The toggle will have a button and a label that changes based on the state.
Step 1: Create the Context
The parent component will manage the state and provide it to the children through React's Context API.
javascript
Copy code
import React, { useState, createContext, useContext } from 'react'; const ToggleContext = createContext(); function Toggle({ children }) { const [on, setOn] = useState(false); const toggle = () => setOn(!on); return ( <ToggleContext.Provider value={{ on, toggle }}> {children} </ToggleContext.Provider> ); }
Here, the Toggle component manages the on state and provides it, along with the toggle function, to its children via the ToggleContext.
Step 2: Create Sub-Components
Next, we define the sub-components that will consume the context provided by Toggle.
javascript
Copy code
function ToggleButton() { const { on, toggle } = useContext(ToggleContext); return <button onClick={toggle}>{on ? 'Turn Off' : 'Turn On'}</button>; } function ToggleLabel() { const { on } = useContext(ToggleContext); return <span>{on ? 'The toggle is ON' : 'The toggle is OFF'}</span>; }
The ToggleButton and ToggleLabel components both consume the ToggleContext to access the on state and the toggle function. This allows them to display the correct label and change the toggle state when the button is clicked.
Step 3: Compose the Components
Finally, you can use the Toggle component along with its sub-components in a declarative way.
javascript
Copy code
function App() { return ( <Toggle> <ToggleButton /> <ToggleLabel /> </Toggle> ); }
This structure makes it clear how the components are related and what each component does. The App component only needs to focus on how to compose the UI, while the Toggle component takes care of the logic and state management.
Advantages of the Compound Component Pattern
Increased Modularity: Each part of the compound component can be developed and tested in isolation, improving the overall modularity of the codebase.
Ease of Use: The API exposed by the compound component is intuitive and easy to use, as it closely resembles the structure of the final UI.
Enhanced Customization: Users of the compound component can easily swap out or omit sub-components, allowing for a high degree of customization without altering the underlying logic.
When to Use the Compound Component Pattern
While the Compound Component Pattern is powerful, it’s not always the best choice. It’s particularly useful when:
You have a group of related components that need to share state or logic.
You want to create a flexible and customizable API for your component.
You need to maintain a clean separation between logic and presentation.
However, for simpler cases where components don’t need to share much logic or state, a simpler pattern, such as passing props directly, might be more appropriate.