---
# System prepended metadata

title: React component design patterns for 2022

---

# React component design patterns for 2022

This list includes some of the most popular React design patterns that are efficient for **cross-cutting concerns**, **global data sharing** (without prop drilling), **the separation of concerns** such as complex stateful logic from other component parts, and more.

> **Cross-cutting concerns:**
> 
> 
> They are **parts of a program that rely on or must affect many other parts of the system**.
> 
> ![](https://i.imgur.com/L3rxuHj.png)



## [The higher-order component pattern](https://blog.logrocket.com/react-design-patterns/#higher-order-component-pattern)

The higher-order component, or HOC pattern, is an advanced React pattern used for **reusing component logic across our application**.

- Examples: authorization, logging, data retrieval

```jsx
import React, {Component} from 'react';

const higherOrderComponent = (DecoratedComponent) => {
  class HOC extends Component {
    render() {
      return <DecoratedComponent />;
    }
  }
  return HOC;
};
```

## [Render props component pattern](https://blog.logrocket.com/react-design-patterns/#render-props-pattern)

We’ll explore yet another way to make React components reusable across our application by implementing the [Render props pattern](https://reactjs.org/docs/render-props.html).

- e.g. we had a `TextInput` whose value we want to share with two components
    
    ```jsx=

    // the input component
    import { useState } from "react";
    const TextInput = (props) => { //*
      const [value, setValue] = useState("");
      return (
        <>
          <input
            type="text"
            value={value}
            onChange={(e) => setValue(e.target.value)}
            placeholder="Type text..."
          />
          {props.children(value)} //*
        </>
      );
    };
    export default TextInput;
    
    // create the two-component to access the value of the input component
    const Comp1 = ({ value }) => <p>{value}</p>;
    const Comp2 = ({ value }) => <p>{value}</p>;
    
    // then use them like this:
    <TextInput>
      {(value) => (
        <>
          <Comp1 value={value} />
          <Comp2 value={value} />
        </>
      )}
    </TextInput>
    ```
    
    :::info
    The state of the `TextInput` is supposed to be in the parent component, but in larger applications, it’s often difficult to perform state lifting.
    :::
    

## [State reducer pattern](https://blog.logrocket.com/react-design-patterns/#state-reducer-pattern)

The state reducer pattern has gotten really popular since the release of `react hooks`.

It has **an abstraction of Redux workflow** using the `useReducer` hook.

```jsx
// create a type for our reducer
const toggleActionTypes = {
  toggle: "TOGGLE",
};

// create the toggleReducer
const toggleReducer = (state, action) => {
  switch (action.type) {
    case toggleActionTypes.toggle:
      return { on: !state.on };
    default:
      throw new Error(`Undefined type: ${action.type}`);
  }
};

// then use it in a component like below
const Toggle = () => {
  const [{ on }, dispatch] = useReducer(toggleReducer, { on: false }); //*

  const handleToggle = () => { //*
    dispatch({ type: toggleActionTypes.toggle });
  };

  return (
    <div>
      <button onClick={handleToggle}>{on ? "Off" : "On"}</button>
    </div>
  );
};
export default Toggle;

```

- e.g. The simplest way to demolish the use of a state reducer pattern is to create a custom helper hook: a `useToggle` hook for toggling component states in our application
    
    ```jsx
    // create a type for our reducer
    const toggleActionTypes = {
      toggle: "TOGGLE",
    };
    
    // create the toggleReducer
    const toggleReducer = (state, action) => {
      switch (action.type) {
        case toggleActionTypes.toggle:
          return { on: !state.on };
        default:
          throw new Error(`Undefined type: ${action.type}`);
      }
    };
    
    // create the hook (useToggle)
    const useToggle = ({ reducer = toggleReducer } = {}) => { //*
      const [{ on }, dispatch] = useReducer(reducer, { on: false });
      const toggle = () => dispatch({ type: toggleActionTypes.toggle });
      return [on, toggle];
    };
    
    // then use it in a component like below
    const Toggle = () => {
      const [on, toggle] = useToggle({ //*
        reducer(currentState, action) {
          const updates = toggleReducer(currentState, action);
          return updates;
        },
      });
      return (
        <div>
          <button onClick={toggle}>{on ? "Off" : "On"}</button>
        </div>
      );
    };
    export default Toggle;
    ```
    

## [The provider pattern](https://blog.logrocket.com/react-design-patterns/#provider-pattern)

The provider pattern in React is used to share global data across multiple components in the React component tree.

The provider pattern involves a `Provider` component that holds global data and shares this data down the component tree in the application using a `Consumer` component or a custom Hook.

:::info
The provider pattern is not unique to React; libraries like [React Redux](https://react-redux.js.org/) and MobX implement the provider pattern, too.
:::

- e.g. the provider pattern for React Redux
    
    ```jsx
    import React from 'react'
    import ReactDOM from 'react-dom'
    
    import { Provider } from 'react-redux' //*
    import store from './store'
    
    import App from './App'
    
    const rootElement = document.getElementById('root')
    ReactDOM.render(
      <Provider store={store}> //*
        <App />
      </Provider>,
      rootElement
    )
    ```
    
- e.g. the provider pattern is implemented in the React context API
    
    The React context API uses the provider pattern to solve this problem.
    
    Thus it enables us to share data across the React components tree without prop drilling.
    
    ```jsx
    import { createContext } from "react"; //*
    const LanguageContext = createContext({}); //*
    function GreetUser() {
      return (
        <LanguageContext.Consumer> //*
          {({ lang }) => (
            <p>Hello, Kindly select your language. Default is {lang}</p>
          )}
        </LanguageContext.Consumer>
      );
    }
    export default function App() {
      return (
        <LanguageContext.Provider value={{ lang: "EN-US" }}> //*
          <h1>Welcome</h1>
          <GreetUser />
        </LanguageContext.Provider>
      );
    }
    ```
    

## [The compound components pattern](https://blog.logrocket.com/react-design-patterns/#compound-components-pattern)

Compound components is an advanced React container pattern that provides a simple and efficient way for multiple components to share states and handle logic — working together.

It enables a parent component to interact and share state with its children implicitly, which makes it suitable for building declarative UI.

- e.g. the`select` and `options` HTML elements:
    
    In the code above, the `select` element manages and shares its state implicitly with the `options` elements. Consequently, although there is no explicit state declaration, the `select` element knows what option the user selects.
    
    ```jsx
    <select>
      <option value="javaScript">JavaScript</option>
      <option value="python">Python</option>
      <option value="java">Java</option>
    </select>
    ```
    

## ⚠️ (not recommended) [The presentational and container component patterns](https://blog.logrocket.com/react-design-patterns/#presentational-container-component-patterns)

These terms were originally coined by [Dan Abramov](https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0). However, he **does not promote** these ideas anymore.

Both the presentational and container patterns are useful because they help us separate concerns. (e.g., complex stateful logic, from other aspects of a component.)

However, because React Hooks enable us to separate concerns without any arbitrary division, the Hooks pattern is recommended instead of the presentational and container component pattern.

### The presentational components: data and view

The presentational components are stateless functional components that are **only concerned with rendering data to the view**. 

And **they have no dependencies on the other parts of the application**.

- e.g. a presentational component is a component that renders a list
    
    ```jsx
    const usersList = ({users}) => {
      return (
      <ul>
          {users.map((user) => (
          <li key={user.id}>
              {user.username}
          </li>
          ))}
      </ul>
      );
    };
    ```
    

### The container components: internal state, life cycle, logic and **presentational components**

Container components are useful **class components** that **keep track of their internal state and life cycle**.

They also **contain presentational components and data-fetching logic**.

- e.g. a container component
    
    ```jsx
    class Users extends React.Component {
      state = {
        users: []
      };
    
      componentDidMount() {
        this.fetchUsers();
      }
    
      render() {
        return (); // ... jsx code with presentation component
      }
    }
    ```
    

## [The Hooks pattern](https://blog.logrocket.com/react-design-patterns/#hooks-pattern)

The React Hooks API gives React functional components a simple and direct way to access common React features such as **props, state, context, refs, and lifecycle**.

The result of this is that functional components do not have to be dumb components anymore as they can use state, hook into a [component lifecycle](https://reactjs.org/docs/react-component.html#the-component-lifecycle), perform side effects, and more from a functional component. These features were originally only supported by class components.

# References
[React component design patterns for 2022](https://blog.logrocket.com/react-component-design-patterns-2022/)

# Memo
- [React Hook Form](https://react-hook-form.com/)