MM
lights

The Container and Presentation Pattern

#2Articles1Week#React#design patterns#JavaScript

Have you ever built a React application and ended up with components that felt cluttered or hard to understand? You might be mixing presentation logic (how things look) with business logic (how things work). This can make your code harder to maintain and test.

The Container and Presentation pattern is a way to separate these concerns, leading to cleaner, more manageable components.

What is the Container and Presentation Pattern?

Imagine a restaurant menu. The menu itself (the presentation component) shows you the dishes (data) and their descriptions. But the kitchen (the container component) is where the actual cooking (business logic) happens. They work together to deliver a delicious meal (functioning React application)!

In React, the container component handles:

  • Fetching data from an API or store

  • Performing calculations or computations

  • Managing application state

The presentation component handles:

  • Displaying the data or computed values on the UI

  • Responding to user interactions (like button clicks)

Benefits of using the Container and Presentation Pattern:

  • Improved Readability: Separating concerns makes your code easier to understand and follow.

  • Increased Reusability: Presentation components can be reused across different parts of your application.

  • Easier Testing: You can test container and presentation components independently.

Example:

An example of Container and presentation pattern is shown below:

import React, { useEffect } from "react";
import CharacterList from "./CharacterList";

const StarWarsCharactersContainer: React.FC = () => {
  const [characters, setCharacters] = useState<Character>([]);
  const [isLoading, setIsLoading] = useState<boolean>(false);
  const [error, setError] = useState<boolean>(false);

  const getCharacters = async () => {
    setIsLoading(true);
    try {
      const response = await fetch(
        "https://akabab.github.io/starwars-api/api/all.json",
      );
      const data = await response.json();
      setIsLoading(false);
      if (!data) return;
      setCharacters(data);
    } catch (err) {
      setError(true);
    } finally {
      setIsLoading(true);
    }
  };

  useEffect(() => {
    getCharacters();
  }, []);

  return (
    <CharacterList loading={loading} error={error} characters={characters} />
  );
};

export default StarWarsCharactersContainer;
// the component is responsible for displaying the characters

import React from "react";
import { Character } from "./types";

interface CharacterListProps {
  loading: boolean;
  error: boolean;
  users: Character[];
}

const CharacterList: React.FC<CharacterListProps> = ({
  loading,
  error,
  characters,
}) => {
  if (loading && !error) return <div>Loading...</div>;
  if (!loading && error)
    return <div>error occurred.unable to load characters</div>;
  if (!characters) return null;

  return (
    <ul>
      {characters.map((user) => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
};

export default CharacterList;

Great to see you here! Subscribe for more content like this delivered straight to your inbox, and let's chat in the comments. Share your thoughts and experiences!