React design patterns (part 2)
Tags: javascript, react, webdev, design
Published: 2021-01-23 12:57:59

![alt text](https://bugfender.com/wp-content/uploads/2021/01/React-II-scaled.jpg)

This article originally appeared at bugfender.com: [React Design Patterns (Part 2)](https://bugfender.com/blog/react-design-patterns-part-2).

This article is the second part of the React Design Patterns article. If you missed the first part, go to part 1 of the series.

This time we'll be talking about the `Context` pattern, the `Presentational and Container Components` pattern, and the  `Compound Components` pattern.

## Context

According to the [React documentation](https://reactjs.org/docs/context.html):

> Context provides a way to pass data through the component tree without having to pass props down manually at every level.

In simple terms, If you have a global state that needs to pass through several component levels, you can use `Context`. For example: if you have a `theme` that influences all the components, `Context` will streamline the process.

**Note.** There is one potential snag to bear in mind when using `Context`: it can make the components less reusable. The `Context` data will be available in the `Provider` scope, so you can't use it outside the `Provider`. I found a great [video](https://www.youtube.com/watch?v=3XaXKiXtNjw&ab_channel=ReactTraining) that explains this issue and tells you how to avoid 'prop drilling.'

Let's see an example of Context in action:

```jsx
import React, { useContext, createContext } from "react";
import "./styles.css";
let data = {
  title: "Welcome"
};
const Context = createContext();

export default function App() {
  return (
    <Context.Provider value={data}>
      <div className="App">
        <Card />
      </div>
    </Context.Provider>
  );
}

const Card = () => {
  return (
    <div className="card">
      <CardItem />
    </div>
  );
};

const CardItem = () => {
  return (
    <div className="CardItem">
      <Title />
    </div>
  );
};

const Title = () => {
  const data = useContext(Context);
  return <h1>{data.title}</h1>;
};
```

As we can see in this (elementary) example, we have three levels of components, and we only use the `data.title` in the last level. This way, we don't need to pass the props to all the levels.

### A few tips on context syntax

I always apply this syntax when using context. However, there are some things I found out when I wrote it again:

- In the case of "static data" (like the example), we actually don't need the `Provider`. we can fulfil that function ourselves:

```jsx
let data = {
  title: "Welcome"
};
const Context = createContext(data);

export default function App() {
  return (
    <div className="App">
      <Card />
    </div>
  );
}
```

At the other end of the scale, we can use the `Customer` instead of `useContext`, like this:

```jsx
const Title = () => {
  return (<Context.Consumer>
            {(data) => <h1>{data.title}</h1>}
        </Context.Consumer>);
};
```

## Presentational and Container Components

These components (also known as `Smart And Dumb Components`) are among the best-known React patterns. There are no references to them in the React documentation,  but Dan Abramov's [article](https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0) provides an excellent guide.

In simple terms, `Presentational And Container Components` refer to the separation of the business logic components from the UI views.

Let's look at another scenario:

- We need to build a `Card` component.
- Inside the card, we have three other components: `Title`, `Image` and `Button`.
- The button changes the picture after a click on it.

Before we start working on our components, let's create two folders: 'Presentational' and 'Container.' Now, let's build the three `Presentational` components :

**Title.js :**

```jsx
import React from "react";
export default function Title(props) {
  const { children, ...attributes } = props;
  return <h1 {...attributes}>{children}</h1>;
}
```

**Image.js :**

```jsx
import React from "react";
export default function Image(props) {
  const { src, alt } = props || {};
  return <img src={src} alt={alt} />;
}
```

**Button.js :**

```jsx
import React from "react";
export default function Button(props) {
  const { children, ...attributes } = props;
  return <button {...attributes}>{children}</button>;
}
```

Finally, we can build in the Container folder component, known as the `Card`.

**Card.js :**

```jsx
import React, { useEffect, useState } from "react";
import Title from "../Presentational/Title";
import Image from "../Presentational/Image";
import Button from "../Presentational/Button";

export default function Card() {
  const [card, setCard] = useState({});
  const [srcIndex, setSrcIndex] = useState(0);

  useEffect(() => {
    setCard({
      title: "Card Title",
      image: {
        imagesArray: [
          "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTh87QN4DkF7s92IFSfm7b7S4IR6kTdzIlhbw&usqp=CAU",
          "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRjFnHdaH1i1m_xOaJfXTyq4anRFwRyCg1p1Q&usqp=CAU"
        ],
        alt: "card image"
      }
    });
  }, []);
  const { image } = card;
  const changeImage = () =>
    setSrcIndex((index) =>
      index < image.imagesArray.length - 1 ? index + 1 : index - 1
    );
  return (
    <div className="card">
      <Title className="title-black">{card.title && card.title}</Title>
      <Image
        src={image && image.imagesArray[srcIndex]}
        alt={image && image.alt}
      />
      <Button onClick={changeImage}>Change Picture</Button>
    </div>
  );
}
```

If you want to see the full code, check it out [here](https://codesandbox.io/s/presentational-and-container-component-example-9df6h).

**Note!** Many of you may be wondering why you needed to separate into different components. You could just write them inside `Card`, right?

Well when we separate the components, we can reuse them anywhere. But even more importantly, it is much easier to implement other patterns like `HOC` or `Render Props`.

## Compound Components

In my opinion, this is one of the most intricate patterns to understand, but I will try to explain it as simply as I can. 

When we talk about `Compound Components`, the most simple way is to think about `select` and `option` in HTML. You can look at them as a group of components that have a basic functionality. There are states that are managed globally (like in the `context` pattern) or from the container (like in `presentational and container` patterns). 

 **`Compound components`** are really a mixture of these two. It's almost as if they each have their owned states and they manage them from within.

Let's look at the next scenario:

- We need to develop `Select` and `Option` components.
- We want the `Option` to be vivid, with different colors.
- The `Option` color will influence the `Select` color.

Let's see the example:

**App.js**

```jsx
import React from "react";
import "./styles.css";
import Select from "./Select";
import Option from "./Option";

export default function App() {
  return (
    <div>
      <Select>
        <Option.Blue>option 1</Option.Blue>
        <Option.Red>option 2</Option.Red>
        <Option>option 3</Option>
      </Select>
    </div>
  );
}
```

- The `App` renders the `Select` and the `Option` components.
- `Option.Blue` and `Option.Red` are 'colors components.'

**Option.js**

```jsx
sdsdimport React, { useEffect } from "react";

function Option(props) {
  const { children, style, value, setStyle } = props;
  useEffect(() => {
    if (setStyle) setStyle({ backgroundColor: style.backgroundColor });
  }, [setStyle, style]);
  return (
    <option value={value} style={style}>
      {children}
    </option>
  );
}

Option.Blue = function (props) {
  props.style.backgroundColor = "blue";
  return Option(props);
};

Option.Red = function (props) {
  props.style.backgroundColor = "red";
  return Option(props);
};
export default Option;
```

- Here you can see the Implementation of `Option.Blue` and `Option.Red`. As will be apparent, we render the `Option` component and just add a property to props.
- The `setStyle` comes from `Select`. It's for changing the select color to the color of the selected option.

**Select.js**

```jsx
import React, { useState } from "react";

export default function Select(props) {
  const { children } = props;
  const [style, setStyle] = useState({});

  const findOptionActive = (e) => {
    const index = e.target.value * 1;
    const optionStyle = { ...e.nativeEvent.target[index].style };
    if (optionStyle) setStyle({ backgroundColor: optionStyle.backgroundColor });
  };

  const childrenWithProps = React.Children.map(children, (child, index) => {
    return React.cloneElement(child, {
      ...child.props,
      value: index,
      setStyle:
        index === 0 && Object.keys(style).length === 0 ? setStyle : null,
      style: { backgroundColor: "white" }
    });
  });

  return (
    <select onChange={findOptionActive} style={style}>
      {childrenWithProps}
    </select>
  );
}
```

- Now, we have a select function with the attributes of `onChange` `style`.
- `findOptionActive` gets the style of the option and changes the style of select accordingly,
- The magic really happens in `childrenWithProps`. Normally, when `Select` receives `children`, we can't access the child props - but with the help of `React.Children` and `React.cloneElement` we can do it. As you can see, we can pass `value`, `setStyle`, and `style` as props.

To get the [Full Code](https://codesandbox.io/s/compound-components-xw3b2?file=/src/App.js), click here.

This exercise gives you good practice, and if you want to try it yourself (maybe in another pattern), add your solution in a comment below.

## Conclusion

This article was intended to show you different patterns in React. You don't need to use any of the patterns if you don't want to, but it's good for a developer to know design patterns on any framework or language, to understand different syntax levels when they see a new codebase.

I hope you enjoyed the tutorial and learned something new. If you know any other pattern or have further information on any of the topics mentioned in the article, please add a comment below.