Skip to content

The Compound Components Pattern enables you to provide a set of components that implicitely share state for a simple yet powerful declarative API for reusable components.

Notifications You must be signed in to change notification settings

gorakhjoshi/ReactPatternCompoundComponents

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Compound Components

📝 Your Notes

Background

One liner: The Compound Components Pattern enables you to provide a set of components that implicitely share state for a simple yet powerful declarative API for reusable components.

Compound components are components that work together to form a complete UI. The classic example of this is <select> and <option> in HTML:

<select>
  <option value="1">Option 1</option>
  <option value="2">Option 2</option>
</select>

The <select> is the element responsible for managing the state of the UI, and the <option> elements are essentially more configuration for how the select should operate (specifically, which options are available and their values).

Let's imagine that we were going to implement this native control manually. A naive implementation would look something like this:

<CustomSelect
  options={[
    { value: '1', display: 'Option 1' },
    { value: '2', display: 'Option 2' },
  ]}
/>

This works fine, but it's less extensible/flexible than a compound components API. For example. What if I want to supply additional attributes on the <option> that's rendered, or I want the display to change based on whether it's selected? We can easily add API surface area to support these use cases, but that's just more for us to code and more for users to learn. That's where compound components come in really handy!

Real World Projects that use this pattern:

Exercise

Every reusable component starts out as a simple implementation for a specific use case. It's advisable to not overcomplicate your components and try to solve every conceivable problem that you don't yet have (and likely will never have). But as changes come (and they almost always do), then you'll want the implementation of your component to be flexible and changeable. Learning how to do that is the point of much of this workshop.

This is why we're starting with a super simple <Toggle /> component.

In this exercise we're going to make <Toggle /> the parent of a few compound components:

  • <ToggleOn /> renders children when the on state is true
  • <ToggleOff /> renders children when the on state is false
  • <ToggleButton /> renders the <Switch /> with the on prop set to the on state and the onClick prop set to toggle.

We have a Toggle component that manages the state, and we want to render different parts of the UI however we want. We want control over the presentation of the UI.

🦉 The fundamental challenge you face with an API like this is the state shared between the components is implicit, meaning that the developer using your component cannot actually see or interact with the state (on) or the mechanisms for updating that state (toggle) that are being shared between the components.

So in this exercise, we'll solve that problem by providing the compound components with the props they need implicitly using React.cloneElement.

Here's a simple example of using React.Children.map and React.cloneElement:

function Foo({ children }) {
  return React.Children.map(children, (child, index) => {
    return React.cloneElement(child, {
      id: `i-am-child-${index}`,
    });
  });
}

function Bar() {
  return (
    <Foo>
      <div>I will have id "i-am-child-0"</div>
      <div>I will have id "i-am-child-1"</div>
      <div>I will have id "i-am-child-2"</div>
    </Foo>
  );
}

Extra Credit

1. 💯 Support DOM component children

A DOM component is a built-in component like <div />, <span />, or <blink />. A composite component is a custom component like <Toggle /> or <App />.

Try updating the App to this:

function App() {
  return (
    <div>
      <Toggle>
        <ToggleOn>The button is on</ToggleOn>
        <ToggleOff>The button is off</ToggleOff>
        <span>Hello</span>
        <ToggleButton />
      </Toggle>
    </div>
  );
}

Notice the error message in the console and try to f

About

The Compound Components Pattern enables you to provide a set of components that implicitely share state for a simple yet powerful declarative API for reusable components.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published