App Masters

App Masters

  • English
  • Português

›Design Patterns

App Masters - Core

  • Home
  • Content

Backend

  • Node Express
  • Node Worker Threads

Database

  • GraphQL

Javascript Fundamentals

  • Asynchronous Javascript
  • Typescript

Design Patterns

  • Presentational Container Design Pattern

Frontend

  • AMP (Accelerated Mobile Pages)
  • Bubblewrap
  • Gatsby.js
  • Next.js
  • PWA
  • Styled Components

Mobile

  • React Native
  • Store Testing

Server

  • Netlify

Testing

  • Cypress
  • Jest

Design

  • Human Interface
  • Material Design

Tools

  • Bitrise
  • Docker

Presentational Container Design Pattern

What is it

Presentational Container Component is a coding design pattern that consists of separating the logic from the presentation code. In a React context it works by splitting a Component into a Presentation part responsible for presenting the data to the user, generally using the JSX markup, and a Container part concerned with loading and processing the data before passing it to the UI.

This pattern helps organize the code and create reusable components that are easier to maintain and can be used in the entire application. It works well with Component Driven Development!

How to apply it

Most of the time, applying this pattern is about splitting complex components into two parts.

Presentation Components

These are responsible for how the user interface looks, generally they don't have any states and don't know how to load or update the data they render. Although they shouldn't store data, this doesn't mean they cannot have states, but when they do, the states should be related to the UI.

Container Components

They are concerned with how the things works and are responsible for handling the logic and specifying the data that a presentational component should render. Here is where you would store most of the states, fetch the data, dispatch actions, and so on.

Simple Example

Take the following component as an example:

export const MainTodoList = () => {
  const dispatch = useDispatch();
  const unsortedTodos = useSelector<StoreType, ToDo[]>(
    store => store.todosReducer.todos
  );
  
  const todos = useMemo(() => [...unsortedTodos].sort(
    (a, b) => a.completed && !b.completed ? -1 : 1
  ), [unsortedTodos]);


  const handleCheck = useCallback((todo: ToDo) => {
    dispatch(doUpdateTodo({ ...todo, completed: !todo.completed }));
  }, [dispatch]);

  const handleDelet = useCallback((todo: ToDo) => {
    dispatch(doDeleteTodo(todo));
  }, [dispatch]);

  return (
    <ul>
      {todos.map(todo =>
        <li key={todo.id}>
          <button
            active={todo.completed}
            onClick={() => handleCheck(todo)}
          >
            <CheckIcon />
          </button>
          <span>{todo.title}</span>
          <button onClick={() => handleDelet(todo)}>
            <CloseIcon />
          </button>
        </li>
      )}
    </ul>
  );
};

It is a single component that handles both data manipulation using a Flux pattern and renders the user interface for the list of Todos.

Here is one way that one could split the component into presentational and container parts:

Presentational

export const TodoList = ({ todos, onCheck, onDelete }) => (
  <ul>
    {todos.map(todo =>
      <li key={todo.id}>
        <button
          active={todo.completed}
          onClick={() => onCheck(todo)}
        >
          <CheckIcon />
        </button>
        <span>{todo.title}</span>
        <button onClick={() => onDelete(todo)}>
          <CloseIcon />
        </button>
      </li>
    )}
  </ul>
);

The presentational component is free of states and calls to the flux actions. It can easily be reused in other parts of the application and tested with tools like Storybook.

Container

export const MainTodoListContainer = () => {
  const dispatch = useDispatch();
  const unsortedTodos = useSelector<StoreType, ToDo[]>(
    store => store.todosReducer.todos
  );
  
  const todos = useMemo(() => [...unsortedTodos].sort(
    (a, b) => a.completed && !b.completed ? -1 : 1
  ), [unsortedTodos]);


  const handleCheck = useCallback((todo: ToDo) => {
    dispatch(doUpdateTodo({ ...todo, completed: !todo.completed }));
  }, [dispatch]);

  const handleDelete = useCallback((todo: ToDo) => {
    dispatch(doDeleteTodo(todo));
  }, [dispatch]);

  return (<TodoList todos={todos} onCheck={handleCheck} onDelete={handleDelete} />
};

The Container contains all the logic and specificities of the data processing that should feed the presentational component. It does not need to worry about how the data will be displayed, only how to format the data so the component can render it.

How we use it today

Most of our recent projects use this pattern somehow, it is easier to develop components in isolation and employ a Storybook first workflow. The Phormar project is a great example of our use of the pattern. Our views are split into and index (Container) and template (Presentational) files. Take a look at the metrics index and template for example.

How to learn more

Useful Material

  • Dan Abramov - Presentational and Container Components
  • Using Presentational and Container Components with Redux
  • Video - React Design Patterns: Presentational and Container Components

Validating your knowledge

  • Refactor or create a simple project replacing complex components with presentational and container components.
← TypescriptAMP (Accelerated Mobile Pages) →
  • What is it
  • How to apply it
    • Presentation Components
    • Container Components
    • Simple Example
    • Presentational
    • Container
  • How we use it today
  • How to learn more
    • Useful Material
    • Validating your knowledge
App Masters
App masters
Content
More
BlogGitHub
Copyright © 2022 App Masters