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.