DEV Community

Cover image for React Design Pattern Container/Presentational Pattern
Ogasawara Kakeru
Ogasawara Kakeru

Posted on

React Design Pattern Container/Presentational Pattern

In React, one way to enforce separation of concern is by using Container/Presentational Pattern.
With this pattern, we can separate the view from the application logic.

For instance, here is a DogImages component that renders dogs images fetched from the Api.

// DogImagesContainer.js
import React, { useState, useEffect } from "react";
import DogImages from "./DogImages";

const DogImagesContainer = () => {
  const [dogs, setDogs] = useState([]);

  useEffect(() => {
    fetch("https://dog.ceo/api/breed/labrador/images/random/6")
      .then(res => res.json())
      .then(({ message }) => setDogs(message));
  }, []); 

  return <DogImages dogs={dogs} />;
};

export default DogImagesContainer;
Enter fullscreen mode Exit fullscreen mode
import React from "react";

export default function DogImages({ dogs }) {
  return dogs.map((dog, i) => <img src={dog} key={i} alt="Dog" />);
}
Enter fullscreen mode Exit fullscreen mode

We want to separate this component into two components.

  1. Presentational Components : Components that care about how data is shown to the user. In this example, that's the rendering the list of dog images.
  2. Container Components : Components that are care about what data is shown to the user. In this example, that's fetching the dog images.

Container Components

The primary function of container components is to pass data to presentational components, which they contain. Container components themselves usually don’t render any other components besides the presentational components that care about their data. Since they don’t render anything themselves, they usually do not contain any styling either.

In our example, we want to pass dog images to the DogsImages presentational component. Before being able to do so, we need to fetch the images from an external API. We need to create a container component that fetches this data, and passes this data to the presentational component DogImages in order to display it on the screen.

Hooks
In many cases, the Container/Presentational pattern can be replaced with React Hooks. The introduction of Hooks made it easy for developers to add statefulness without needing a container component to provide that state.

Instead of having the data fetching logic in the DogImagesContainer component, we can create a custom hook that fetches the images, and returns the array of dogs.

export default function useDogImages() {
  const [dogs, setDogs] = useState([]);

  useEffect(() => {
    fetch("https://dog.ceo/api/breed/labrador/images/random/6")
      .then((res) => res.json())
      .then(({ message }) => setDogs(message));
  }, []);

  return dogs;
}

Enter fullscreen mode Exit fullscreen mode
import React from "react";
import useDogImages from "./useDogImages";

export default function DogImages() {
  const dogs = useDogImages();

  return dogs.map((dog, i) => <img src={dog} key={i} alt="Dog" />);
}
Enter fullscreen mode Exit fullscreen mode

By using this hook, we no longer need the wrapping DogImagesContainer container component to fetch the data, and send this to the presentational DogImages component. Instead, we can use this hook directly in our presentational DogImages component!

By using the useDogImages hook, we still separated the application logic from the view. We’re simply using the returned data from the useDogImages hook, without modifying that data within the DogImages component.

Hooks make it easy to separate logic and view in a component, just like the Container/Presentational pattern. It saves us the extra layer that was necessary in order to wrap the presentational component within the container component.

Top comments (0)