Compound components are a design pattern in React, where components are used together to accomplish a specific task. These components share an internal state to communicate with each other. Typically, a parent component manages the state and behaviour, while child components encapsulate the presentation and functionality of specific parts of the overall component.
Think of it like the <select>
and <option>
tags in HTML:
The select
tag works together with the option
tag which is used for a drop-down menu to select items in HTML. Here the select
element manages the state, and we see what elements are passed as options to the select
. This communication between the parent and child is what defines a compound component. Compound components in React are used to build a declarative UI component which helps to avoid prop drilling.
Creating Compound Components
Suppose we want to build a custom select component with identical behavior, thus it contains a list of options and only one option can be selected at once.
Let's see, how we can implement this without using compound components:
The current implementation tightly couples the Option
component with the Select
component. If you want to customize or add functionality to individual options, you will need to expose customization props through the parent component Select
. This violates the principle of separation of concerns and makes the code less reusable and maintainable.
We can address these issues by converting the above Select
and Option
components into a compound component.
First, let’s modify the Select
component. This component maintains the activeOption
state, which is then passed down to all the child components through the SelectContext.Provider
.
To give the Option
component access to the SelectContext provider, we need to render it as a child of the Select
component. Now, we will modify the Options
component to receive the activeOption
state and setActiveOption
function from the context. Upon clicking an option, we will update the activeOption
state with the current value.
Also, we will make the Option
component a property of the Select
component. This way, we won't need to explicitly import the Option
component whenever we want to use the Select
component in any file. Instead, we'll only need to import Select
.
Now we can use Option
as the property of the Select
component. Let's use the Select
component to render two items using the Select.Option
component:
Benefits of compound components
Compound components manage their own internal state, which they share among the several child components. When implementing a compound component, we don’t have to worry about managing the state ourselves. The components API gives you a nice way to express relationships between components.
Furthermore, when importing a compound component, we don’t have to explicitly import the child components that are available on that component.