The Best React Design Patterns You Should Know About in 2024

The Best React Design Patterns You Should Know About 1

There is no denying the immense popularity and practicality of React. For a long time, most web design was built with CSS, HTML, and JavaScript. React brought a much-needed sigh of relief for developers with its ease of use. The reusable components, great developer tools, and extensive ecosystem are some of the most loved features of React.

Instead of the traditional approach of directly manipulating the DOM, React introduced a useful level of abstraction in the form of the virtual DOM concept. 

The library is being actively developed and maintained by React developers at the tech giant Facebook. This provides it with a much-needed edge over other frameworks and libraries.  Countless contributors in the JavaScript community also regularly contribute to refining and improving React. 

All these factors allow React to maintain its popularity among developers even though newer frameworks are constantly emerging and competing for recognition amongst frontend developers.

code design developer

There are numerous design patterns that are available in React.js. Here, we shortlist a few recommended React patterns that you should definitely know about when building web apps. 

Build prototypes with UI components from a Git repository, Storybook or through an npm. Bring the components to our design editor and create stunning layouts without designers. Request access to UXPin Merge.

Create beautiful layouts without designers

Design production-ready prototypes 8.6x faster. No pixels. pure code.

Why You Should Follow React Design Patterns?

Let us first briefly recap the role that design patterns play. Simply put, design patterns are repeatable solutions to commonly occurring problems in software development.

They serve as a basic template upon which you can build up the program’s functionality according to the given requirements. 

The term ‘design pattern’ is not to be confused with a ‘design system’. We have discussed more design systems in a separate article.

designops picking tools care

Design patterns not only speed up the development process but also make the code easier to read and to maintain. 

Some common examples of design patterns include the Singleton pattern and the Gang-of-Four pattern.

In software development, design patterns are associated with two common roles.

  1. Design patterns offer a common platform to developers.
  2. Design patterns ensure that React best practices are applied.

Let’s look at them closer.

Role #1: Offer a common platform to developers

Design patterns provide standard terminology and solutions to known problems. Let us take the example of the Singleton pattern that we mentioned above. 

This pattern postulates the use of a single object. Developers implementing this pattern can easily communicate to other developers that a particular program follows the singleton pattern and they will understand what this means. 

Role #2: Ensure that React best practices are applied

Design patterns have been created as a result of extensive research and testing. They not only allow developers to become easily accustomed to the development environment but also ensure that the best practices are being followed.

This results in fewer errors and saves time during debugging and figuring out problems that could have been easily avoided if an appropriate design pattern had been implemented.

Like every other good programming library, React makes extensive use of design patterns to provide developers a powerful tool. By properly following the React philosophy, developers can produce some extraordinary applications.  

Now that you have an understanding of design patterns. Let us move on to some of the most widely used design patterns available in React.js. 

Functional vs Class Components

Components can be of two types, namely, functional and class components. The difference between the two is explained in a separate blog article: Functional vs Class Components – What Do You Need to Know?

Class Components

Class components are built upon JavaScript classes, extending the React.Component class. They are the traditional approach to building React components and offer a robust structure for managing state and lifecycle events. Class components are particularly advantageous for complex scenarios where precise control over state and lifecycle behavior is essential.

In a class component, you define a class that inherits from React.Component. This class can contain a constructor for initializing state, lifecycle methods for handling various stages of a component’s existence, and a render method to define the component’s UI. This structured approach is beneficial when dealing with intricate components that require meticulous management of internal state and lifecycle events.

Here’s an example of a React class component:

import React, { Component } from 'react';

class MyClassComponent extends Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0,
    };

    // Binding 'this' context to the method
    this.incrementCount = this.incrementCount.bind(this);
  }

  incrementCount() {
    this.setState((prevState) => ({
      count: prevState.count + 1,
    }));
  }

  render() {
    return (
      <div>
        <h1>Count: {this.state.count}</h1>
        <button onClick={this.incrementCount}>Increment</button>
      </div>
    );
  }
}

export default MyClassComponent;

Functional Components

On the other hand, functional components resemble typical JavaScript functions. They receive properties (props) as arguments and return React elements for rendering. While initially lacking some features present in class components, functional components have gained prominence with the introduction of React Hooks.

Functional components are well-suited for simpler scenarios, promoting a more functional programming approach. With the advent of Hooks, functional components can now handle state and lifecycle events, eliminating the need for class components in many cases. The concise syntax and ease of understanding make functional components an excellent choice for straightforward UI elements.

Here’s an example of a React functional component using hooks:

import React, { useState } from 'react';

const MyFunctionalComponent = () => {
  const [count, setCount] = useState(0);

  const incrementCount = () => {
    setCount(prevCount => prevCount + 1);
  };

  return (
    <div>
      <h1>Count: {count}</h1>
      <button onClick={incrementCount}>Increment</button>
    </div>
  );
};

export default MyFunctionalComponent;

Compound Pattern

When React developers have two or more components that work together, more likely one is the parent while the rest are children. But did you know that you can make them share states and handle logic together?

That’s what the compound component React pattern is all about. The compound components API shows relationships between components and allows them to communicate in a flexible way.

If you want to know more about it, read this article by LogRocket about understanding React compound components.

Here’s an

example of the compound component pattern with a simplified Accordion component:

import React, { useState, createContext, useContext } from 'react';

// Create a context to share state between compound components
const AccordionContext = createContext();

const Accordion = ({ children }) => {
  const [openIndex, setOpenIndex] = useState(null);

  const toggleIndex = (index) => {
    setOpenIndex(openIndex === index ? null : index);
  };

  return (
    <AccordionContext.Provider value={{ openIndex, toggleIndex }}>
      <div className="accordion">{children}</div>
    </AccordionContext.Provider>
  );
};

const AccordionItem = ({ children, index }) => {
  const { openIndex, toggleIndex } = useContext(AccordionContext);
  const isOpen = openIndex === index;

  return (
    <div className={`accordion-item ${isOpen ? 'open' : ''}`}>
      <div className="accordion-header" onClick={() => toggleIndex(index)}>
        {children[0]}
      </div>
      {isOpen && <div className="accordion-body">{children[1]}</div>}
    </div>
  );
};

// Usage
const App = () => {
  return (
    <Accordion>
      <AccordionItem index={0}>
        <div>Header 1</div>
        <div>Content 1</div>
      </AccordionItem>
      <AccordionItem index={1}>
        <div>Header 2</div>
        <div>Content 2</div>
      </AccordionItem>
      <AccordionItem index={2}>
        <div>Header 3</div>
        <div>Content 3</div>
      </AccordionItem>
    </Accordion>
  );
};

export default App;

Conditional Rendering

Conditions are the foremost tool in the arsenal of any software developer. 

In the process of writing React components, the need often arises to render a certain JSX code based on the state. This is achieved through conditional rendering.

04 1 1

Conditional rendering is very useful as it allows you to create distinct components based on your needs and then render only the ones that are required by the application.

For instance, conditional rendering can be used to display different messages to the user based on the login status of the user. The message will be subject to the value of the prop isLoggedIn.

Render Props

We discussed how design patterns are there to solve common problems. Render props are available in React to help us solve the problem of logic repetition. 

According to official React documentation, render props are defined as a ‘technique for sharing code between React components using a prop whose value is a function’.

Render props prove really handy as they allow us to share the same state across different components. Instead of hardcoding the logic inside each component, you can use a function prop to determine what to render.

Some popular libraries that make use of render props include Formik, React Router, and Downshift.

Here’s an example demonstrating the Render Props pattern:

import React, { Component } from 'react';

// Component using the Render Props pattern
class Mouse extends Component {
  constructor(props) {
    super(props);
    this.state = { x: 0, y: 0 };

    this.handleMouseMove = this.handleMouseMove.bind(this);
  }

  handleMouseMove(event) {
    this.setState({
      x: event.clientX,
      y: event.clientY
    });
  }

  render() {
    return (
      <div style={{ height: '100vh' }} onMouseMove={this.handleMouseMove}>
        {this.props.render(this.state)}
      </div>
    );
  }
}

// Usage of the Mouse component with render props
const App = () => {
  return (
    <div>
      <h1>Move the mouse around!</h1>
      <Mouse render={({ x, y }) => (
        <h2>The mouse position is ({x}, {y})</h2>
      )}/>
    </div>
  );
};

export default App;

Controlled Components

Web forms are a common requirement in a large number of applications and controlled components are React’s answer to handling form state.

The controlled component takes the state through props. It can notify any changes by means of callbacks like onChange

Parent components can control it by handling the callback and managing its own state meanwhile, the new values are passed to the controlled component as props.

By default React forms have support for both controlled and uncontrolled components. It is highly recommended that you use controlled components. 

The following code snippet shows a controlled component.

<input type = "text" value = {value} onChange = {handleChange} />

React Hooks Pattern

Hooks are a relatively new addition to React and were introduced in React 16.8. 

These functions allow developers to use React without classes. There are a number of different pre-built hooks available like the Effect Hook ( useEffect ) and the State Hook. 

For a complete list of available hooks, you can visit the Hooks API Reference.

Apart from the pre-built hooks in React, you can also create your own hooks. This allows you to extract the component logic and create reusable functions.

Hooks are a welcome addition to React and the developer community really appreciated this new addition with great enthusiasm.

However, it must be kept in mind that sometimes hooks can become a little tricky to work with when the arguments are objects, arrays, or functions. This can become somewhat confusing.

On the other hand, custom hooks are easy and simple to use and they also provide immense benefits to the developer.

Here’s an example of a functional component in React that uses hooks:

import React, { useState, useEffect } from 'react';

const Counter = () => {
  // useState hook to manage the counter state
  const [count, setCount] = useState(0);

  // useEffect hook to perform a side effect: updating the document title
  useEffect(() => {
    document.title = `Count: ${count}`;
  }, [count]); // Only re-run the effect if count changes

  // Function to handle incrementing the count
  const increment = () => {
    setCount(prevCount => prevCount + 1);
  };

  // Function to handle decrementing the count
  const decrement = () => {
    setCount(prevCount => prevCount - 1);
  };

  return (
    <div>
      <h1>Counter: {count}</h1>
      <button onClick={increment}>Increment</button>
      <button onClick={decrement}>Decrement</button>
    </div>
  );
};

export default Counter;

Higher-Order Component Pattern

When it comes to more advanced React patterns, there’s higher-order component pattern, referred to as HOC. It’s applied whenever React developer wants to reuse logic within application.

HOC takes a component as an argument and when it returns it, it adds data and functionality to the component.

For instance, when using React with Redux, you can pass the component through connect function and it will get injected with data from the Redux store. The values that you get will be passed as Props.

HOC is not a part of the core React API. It’s a JavaScript function. Nonetheless, it is in line with the nature of React functional components, that’s composition over inheritance.

Here’s an example of the HOC pattern in React:

import React from 'react';

// Higher-Order Component
const withLogging = (WrappedComponent) => {
  class WithLogging extends React.Component {
    componentDidMount() {
      console.log(`Component ${WrappedComponent.name} is mounted.`);
    }

    componentWillUnmount() {
      console.log(`Component ${WrappedComponent.name} is unmounted.`);
    }

    render() {
      return <WrappedComponent {...this.props} />;
    }
  }

  return WithLogging;
};

// Example component
const MyComponent = (props) => {
  return <div>{props.text}</div>;
};

// Wrap MyComponent with withLogging HOC
const MyComponentWithLogging = withLogging(MyComponent);

// Usage
const App = () => {
  return <MyComponentWithLogging text="Hello, world!" />;
};

export default App;

Use Most Common React Design Patterns

React has proven to be a highly popular library. The community is among the fastest-growing developer communities online.

You will also find lots of useful web development resources available online that make it easy to learn react.js and adapt to it.

The power of React is due to its amazing features and the robust architecture that it offers. One of the most prominent and widely loved features of React is its design patterns.

Design patterns are in fact what gives this library its extraordinary practicality and usefulness. They make code optimization and maintenance easier.

They allow developers to create apps that are flexible in nature, deliver better performance, and produce a codebase that is easier to maintain.

We have discussed a few popular React design patterns like stateless functions, render props, controlled components, conditional rendering, and react hooks. 

However, it must be noted that react design patterns are not just limited to these patterns and there are several different design patterns that you can implement. Once you get familiar with the usage of the common design patterns, it will become easier to graduate to others. 

Build React-Based Prototypes with UXPin Merge

Capturing the true essence of React application development can be made easier by the use of the right technology. With UXPin Merge, you use React code components in UXPin to build powerful prototypes. You can easily put together code-based prototypes that are pure code. Try it for free.

Use a single source of truth for design and development. Discover Merge

by UXPin on 29th May, 2024

UXPin is a web-based design collaboration tool. We’re pleased to share our knowledge here.

Still hungry for the design?

UXPin is a product design platform used by the best designers on the planet. Let your team easily design, collaborate, and present from low-fidelity wireframes to fully-interactive prototypes.

Start your free trial

These e-Books might interest you