# React Design Patterns, Part 1 Compound Components.

- URL: https://www.obytes.com/blog/react-design-patterns
- Published: 2019-12-17
- Author: Bouamar El Ouahhabi
- Topics: Front-end
- Summary: Learn the compound components pattern in React, then make it more flexible with the context API to build components that share state without prop drilling.

When i started my journey as a Junior React Developer, i started to build things from scratch and all goes well for me at first. I needed sometimes
to reiterate over some components to add more requirements and i was able to refactor them for the first iterations, but after a while it
started to be difficult to add functionalities to existing components or to explain the existing code base to a newcomer to the team.
Fortunately a senior newcomer had some good solutions for us that we didn’t understand nor accept at first, but with some effort and
researches all became clear and pleasant again. These new ways of writing code without repeating ourself and that gives more control to the consumer of the components
rather than be usable for one use case. These amusing ways of writing components are called Design Patterns, and we will visit a lot of them
in this serie, so prepare yourself to be a rockstar of the react world.

In this serie we are going to learn about different component patterns by building a simple accordion component.

First we will explore a naive implementation, to understand the problem space, [here is a link](https://codesandbox.io/s/01-base-7w9zt) to the starter code.

```javascript
// index.js

import React from 'react';
import { render } from 'react-dom';

import Accordion from './Accordion';
import { data } from './data';

import 'normalize.css';
import './styles.css';

const App = () => {
  return (
    <div className='App'>
      <Accordion
        items={data} // Required, array of data objects
      />
    </div>
  );
};

const rootElement = document.getElementById('root');
render(<App />, rootElement);
```

```javascript
// Accordion.js

import React, { useState } from 'react';
import AccordionItem from './AccordionItem';
import ActiveItem from './ActiveItem';

const Accordion = ({ items }) => {
  const [activePanel, setActivePanel] = useState(-1);

  const handleClick = (id) => {
    const value = id === activePanel ? -1 : id;
    setActivePanel(value);
  };

  return (
    <>
      <div className='panels'>
        {items.map((item) => (
          <AccordionItem
            key={item.itemId}
            item={item}
            onClick={() => handleClick(item.itemId)}
            activePanel={activePanel}
          >
            <p style={{ fontSize: '15pt', color: 'white' }}>{item.title}</p>
          </AccordionItem>
        ))}
      </div>
      <ActiveItem activePanel={activePanel} items={items} />
    </>
  );
};

export default Accordion;
```

If we assume that we publish the Accordion component to `npm` and the user/consumer of this component will only import it like this:
`import Accordion from "accordion";`,
then it’s flexibility ends at the `items` prop; we are only able to change the items to display.
What if we need to position the `AccordionItem` bellow `ActiveItem`?

Let’s try a different approach and rewire the component so it has flexibility and reusability to be used in any future configurations.

## Compound Components

Compound components are groupings of smaller components that all work together and are exposed as subcomponents of a single root.
Those can be exposed via exports from a folder's index file, or as properties on a main component.

Example usage exposed from an index file:

```javascript
import {
  ParentComponent,
  ChildComponent1,
  ChildComponent2,
} from 'components/page';
<ParentComponent>
  <ChildComponent1>Some content 1.</ChildComponent1>
  <ChildComponent2>Some content 2.</ChildComponent2>
</ParentComponent>;
```

Example usage exposed as properties of a single component:

```javascript
import { ParentComponent } from 'components/page';
<ParentComponent>
  <ParentComponent.ChildComponent1>
    Some content.
  </ParentComponent.ChildComponent1>
  <ParentComponent.ChildComponent2>
    Some content.
  </ParentComponent.ChildComponent2>
</ParentComponent>;
```

Our demos are built using the compound component described in the second example above.

This second example can be acheived by making the child components as static class properties on the larger parent class component

```javascript
class ParentComponent extends React.Component {
  static ChildComponent1 = () => {
    // code here
  };

  static ChildComponent2 = () => {
    // code here
  };

  // code here
}
```

Or if you like the functional way like this

```javascript
const ParentComponent = () => {
  // code here
};

// This is equivalent to static properties for classes

ParentComponent.ChildComponent1 = () => {
  // code here
};

ParentComponent.ChildComponent2 = () => {
  // code here
};
```

Using the compound component pattern, we can let the consumer decide how many or what order they want to render their sub-components.
This idea is called inversion of control and it gives more power back
to the consumer of your component and this makes your component more flexible and more scalable.

Let's go back to our Accordion component as you've noticed the only thing that we exposed as API is the items prop

```javascript
<Accordion
  items={data} // Required, array of data objects
/>
```

This is not a reusable nor a flexible API

Let's use the magic of compound components and see, but before that we need to understand two small utilities from the React library
`React.Children.map` and `React.cloneElement`.

`React.Children.map` is similar to `Array.map` as it's used to map over `this.props.children` and return an array,
but as you know `this.props.children` can be an array if we have more than one child but can also be a single node
element if we have one child,
so `React.Children.map` convert the single node element to an array before mapping over it.

Remember this example

```javascript
import { ParentComponent } from 'components/page';
<ParentComponent>
  <ParentComponent.ChildComponent1>
    Some content.
  </ParentComponent.ChildComponent1>
  <ParentComponent.ChildComponent2>
    Some content.
  </ParentComponent.ChildComponent2>
</ParentComponent>;
```

We use `React.Children.map` like in the render method here:

```javascript
class ParentComponent extends React.Component {
  static ChildComponent1 = () => {
    // code here
  };

  static ChildComponent2 = () => {
    // code here
  };

  render() {
    return React.Children.map(this.props.children, (child) => {
      // code here
    });
  }
}
```

`React.cloneElement(element, additionalProps)` Clone and return a new React element using element as the starting point.
The resulting element will have the original element’s props with the new additionalProps merged in.

```javascript
import OriginalComponent from "OriginalComponent";

const NewComponent = React.cloneElement(OriginalComponent, {{ foo: "bar" }})

```

`NewComponent` is the clone (same definition) of `OriginalComponent` except that it has in addition the prop `foo`.

Using these techniques I am able to design components that are completely reusable,
and have the flexibility to use them in a number of different contexts.

Here is the `Accordion` component with the Compound components pattern:

- Usage

```javascript
// index.js

import React from 'react';
import { render } from 'react-dom';

import Accordion from './Accordion';
import { data as items } from './data';

import 'normalize.css';
import './styles.css';

const App = () => {
  return (
    <div className='App'>
      <div className='panels'>
        <Accordion>
          {items.map((item) => {
            return (
              <Accordion.AccordionItem key={item.itemId} item={item}>
                {item.title}
              </Accordion.AccordionItem>
            );
          })}
        </Accordion>
        <div />
      </div>
    </div>
  );
};

const rootElement = document.getElementById('root');
render(<App />, rootElement);
```

- Definition

```javascript
// Accordion.js

import React, { Component } from 'react';

class Accordion extends Component {
  state = {
    activePanel: -1,
  };
  static AccordionItem = ({ children, item, activePanel, onClick }) => {
    return (
      <div
        className={`panel${
          activePanel === item.itemId ? ' open open-active' : ''
        }`}
        style={{
          backgroundImage: `url("${item.imageUrl}")`,
          height: '500px',
        }}
        onClick={() => onClick(item.itemId)}
      >
        <p>{children}</p>
      </div>
    );
  };

  handleClick = (id) => {
    const value = id === this.state.activePanel ? -1 : id;
    this.setState({ activePanel: value });
  };

  render() {
    const { activePanel } = this.state;
    return React.Children.map(this.props.children, (child) => {
      return React.cloneElement(child, {
        activePanel,
        onClick: this.handleClick,
      });
    });
  }
}

export default Accordion;
```

In line 9, we declared `AccordionItem` as a static proprety of the `Accordion` class component, so it's namespaced, and we
can access it like this `Accordion.AccordionItem` by only importing the class `Accordion`.

In line 33 to 38, we mapped over the childrens of `Accordion` and add to them the `activePanel` state as prop by cloning them,
so from now on, the `activePanel` state is implicit and we can't acceess it when using this API:

```javascript
<Accordion>
  <Accordion.AccordionItem item={item}>{item.title}</Accordion.AccordionItem>
</Accordion>
```

[Here is a link](https://codesandbox.io/s/02-compound-components-gt13h) to the `Accordion` compond component that implements the above functionalities.

## More flexible Compound Components with the help of context API

Suppose for stylistic purpose we want to wrap `<Accordion.AccordionItem />` component in a `div` like this:

```javascript
<Accordion>
  <div className='panels'>
    {items.map((item) => {
      return (
        <Accordion.AccordionItem key={item.itemId} item={item}>
          {item.title}
        </Accordion.AccordionItem>
      );
    })}
  </div>
  <Accordion.ActiveItem items={items}>{item.title}</Accordion.ActiveItem>
</Accordion>
```

This will break our Accordion and the reason is that when we mapped over and cloned children :

```javascript
return React.Children.map(this.props.children, (child) => {
  return React.cloneElement(child, {
    activePanel,
    onClick: this.handleClick,
  });
});
```

When cloning, we gave the additional props to the direct child, which is the `div` element in the example above not the `<Accordion.AccordionItem />` component.

To solve this problem we can use the context API and pass this implicit state to the childs whatever their depth in the hirarchy:

```javascript
// Accordion.js

import React, { Component } from 'react';

const AccordionContext = React.createContext({
  activePanel: -1,
  onClick: () => {},
});

class Accordion extends Component {
  static AccordionItem = ({ children, item }) => {
    return (
      <AccordionContext.Consumer>
        {({ activePanel, onClick }) => (
          <div
            className={`panel${
              activePanel === item.itemId ? ' open open-active' : ''
            }`}
            style={{
              backgroundImage: `url("${item.imageUrl}")`,
              height: '500px',
            }}
            onClick={() => onClick(item.itemId)}
          >
            <p style={{ fontSize: '15pt', color: 'white' }}>{children}</p>
          </div>
        )}
      </AccordionContext.Consumer>
    );
  };

  static ActiveItem = ({ items }) => {
    return (
      <AccordionContext.Consumer>
        {({ activePanel }) =>
          activePanel !== -1 ? (
            <h2>{items.find(({ itemId }) => activePanel === itemId).title}</h2>
          ) : (
            <h2>Select a photo</h2>
          )
        }
      </AccordionContext.Consumer>
    );
  };

  handleClick = (id) => {
    const value = id === this.state.activePanel ? -1 : id;
    this.setState({ activePanel: value });
  };

  state = {
    activePanel: -1,
    onClick: this.handleClick,
  };

  render() {
    return (
      <AccordionContext.Provider value={this.state}>
        {this.props.children}
      </AccordionContext.Provider>
    );
  }
}

export default Accordion;
```

From line 5 to 8, we create the `AccordionContext`, and from line 58 to 60, we gave the `AccordionContext.Provider` the state as a value,
we made the onClick handler in the state for optimisation purpose, and we used this value in the static component with the help of the
`AccordionContext.Consumer`. This way we are able to pass this implicit state in any deeply nested component.

[Here is a link](https://codesandbox.io/s/03-compound-components-zxwz5) to the `Accordion` compond component with the context API.

In the next part of this serie, we will discuss how we can use render props to achieve the same goals without having to rely on
wiring up context to share state between components in our application.
