DEV Community

Abdelrahman Mohamed Allam
Abdelrahman Mohamed Allam

Posted on

4 Design Patterns Every ReactJS Developer Should Know

It's important to understand and apply design patterns to write maintainable and scalable code. In this blog post, we'll explore four essential design patterns for ReactJS developers.

1. Component Pattern

The Component pattern is one of the fundamental design patterns in ReactJS. It involves breaking down an application into smaller, reusable components that can be composed together to create complex user interfaces.

A well-designed component should be self-contained, meaning it should have its own state, behavior, and presentation logic. This makes it easier to test, debug, and maintain.

Here's an example of a simple component that displays a button:


import React from 'react';

function Button(props) {

  return (

    <button onClick={props.onClick}>{props.label}</button>

  );

}

export default Button;

Enter fullscreen mode Exit fullscreen mode

In this example, we've created a simple button component that takes two props: onClick and label. The component returns a button element with an onClick event handler and displays the label passed as a prop.

2. Container Pattern

The Container pattern is a design pattern that separates the presentation logic from the data-fetching logic in a component. This allows us to reuse the same presentation logic with different data sources.

In a Container component, we fetch the data from an external source (e.g., an API) and then pass it down to a Presentational component as props. The Presentational component is responsible for rendering the data in the UI.

Here's an example of a Container component that fetches a list of users from an API and passes it down to a Presentational component:


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

import UserList from './UserList';

function UserListContainer() {

  const [users, setUsers] = useState([]);

  useEffect(() => {

    fetch('/api/users')

      .then(res => res.json())

      .then(data => setUsers(data))

      .catch(error => console.error(error));

  }, []);

  return <UserList users={users} />;

}

export default UserListContainer;

Enter fullscreen mode Exit fullscreen mode

In this example, we've created a Container component (UserListContainer) that fetches a list of users from an API using the fetch function. Once the data is fetched, it's stored in the component state using the useState hook. Finally, the component returns a Presentational component (UserList) and passes the fetched data down as a prop.

3. Higher-Order Component (HOC) Pattern

The Higher-Order Component (HOC) pattern is a design pattern that allows us to reuse component logic across multiple components. An HOC is a function that takes a component as an argument and returns a new component with additional behavior.

HOCs are useful for implementing cross-cutting concerns, such as authentication, logging, or caching. By wrapping a component in an HOC, we can apply the same behavior to multiple components without having to repeat the same code.

Here's an example of an HOC that logs the render time of a component:


import React from 'react';

function withRenderTime(Component) {

  return function WithRenderTime(props) {

    const start = performance.now();

    const result = <Component {...props} />;

    const end = performance.now();

    console.log(`Render time for ${Component.name}: ${end - start}ms`);

    return result;

  };

}

export default withRenderTime;

Enter fullscreen mode Exit fullscreen mode

In this example, we've created an HOC (withRenderTime) that takes a component as an argument and returns a new component (WithRenderTime) that logs the render time of the wrapped component.

4. Render Props Pattern

The Render Props pattern is a design pattern that allows us to share code between components using a prop that provides a render function. This pattern is useful for creating reusable components that can be customized with different rendering logic.

In a Render Props component, we expose a prop that takes a function as an argument and returns the result of that function in the component's render method.

Here's an example of a Render Props component that provides a Mouse component that tracks the mouse position:


import React, { useState } from 'react';

function Mouse(props) {

  const [position, setPosition] = useState({ x: 0, y: 0 });

  function handleMouseMove(event) {

    setPosition({ x: event.clientX, y: event.clientY });

  }

  return (

    <div onMouseMove={handleMouseMove}>

      {props.render(position)}

    </div>

  );

}

export default Mouse;

Enter fullscreen mode Exit fullscreen mode

In this example, we've created a Mouse component that tracks the mouse position and exposes a render prop that takes a function as an argument. The render prop is called with the current mouse position and returns the result of that function in the component's render method.

Conclusion

Design patterns are essential for writing maintainable and scalable code in ReactJS. In this blog post, we've explored four essential design patterns for ReactJS developers: Component, Container, Higher-Order Component (HOC), and Render Props.

By mastering these four design patterns, you'll be able to write more modular, reusable, and maintainable code in ReactJS. Each pattern has its own strengths and use cases, so it's important to choose the right pattern for the job.

Remember, design patterns are not a silver bullet for all problems, but they can provide a solid foundation for building robust and scalable applications in ReactJS. Happy coding!

Top comments (0)