React Design Patterns ⭐

React Design Patterns ⭐

In this article you’ll learn How React Developer can save time and effort by using design patterns. Its Provide a quick approach to find problems using tested and trusted solutions which is helps React Developers create maintainable, scalable and efficient application. in this article. I will explain React Design patterns and explain how they might improve the development of React application


The Design Patten That I will cover in this article this following:

  • Singleton Pattern

  • Proxy Pattern

  • Factory Pattern

  • Observer Pattern

  • Mixin Pattern

  • Module Pattern

  • High Order Component

  • Compound Component


Perquisites

  1. Basic of Html CSS JavaScript

  2. Basic Understand of ReactJS

  3. Basic understanding of State Management context Apis and Hooks like use State, use Effects

Singleton

The Singleton pattern ensures that a class has only one instance and provides a global point of access to it. This pattern can be useful in React for services that should have a single shared instance, like an API service.

Example

const createSingleton = (() => {
    let instance;

    return () => {
        if (!instance) {
            instance = {
                fetchData: () => {
                    // fetch data from the API
                }
            };
        }
        return instance;
    };
})();

// Usage in a component
const MyComponent = () => {
    const apiClient = createSingleton();

    // Use the Api Client for fetching data
};

The Proxy Pattern acts as a middleman or intermediary for another object, managing or controlling access to it. Think of a proxy pattern like having a personal assistant. When someone wants to schedule a meeting with you, they go through your assistant first. The assistant manages your schedule, filters requests based on your preferences, and only lets certain requests through. Similarly, in programming, the proxy pattern controls interactions with an object, adding extra logic or restrictions.

In React Functional Components: In the context of React, a proxy can be used to control or modify the properties (props) passed to a component, or to add additional rendering logic.

For example,

  • We create a useState hook to hold the person object.

  • We use useEffect to set up the proxy once the component mounts.

  • Inside the useEffect, we define the proxy with the given get and set traps.

  • We demonstrate interacting with the proxy by changing the name and logging the age.

  • Finally, we update the state with the proxy. This will re-render the component, reflecting any changes.

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

function PersonComponent() {
  const [person, setPerson] = useState({
    name: "John Doe",
    age: 42,
    nationality: "American"
  });

  useEffect(() => {
    const handler = {
      get: (obj, prop) => {
        console.log(`The value of ${prop} is ${obj[prop]}`);
        return Reflect.get(obj, prop);
      },
      set: (obj, prop, value) => {
        console.log(`Changed ${prop} from ${obj[prop]} to ${value}`);
        return Reflect.set(obj, prop, value);
      }
    };

    const personProxy = new Proxy(person, handler);

    // Example of interacting with the proxy
    personProxy.name = 'Jane Doe';
    console.log(personProxy.age);

    // Update the state with the proxy to trigger re-render
    setPerson(personProxy);

  }, []);

  return (
    <div>
      <h1>Person Details</h1>
      <p>Name: {person.name}</p>
      <p>Age: {person.age}</p>
      <p>Nationality: {person.nationality}</p>
    </div>
  );
}

export default PersonComponent;

Factory Pattern

The Factory Pattern is akin to a manufacturing factory which produces different types of products. In a real-world scenario, think of a car manufacturing factory that can produce various models of cars. The factory decides which type of car to produce based on the specifications given. Similarly, in programming, the Factory Pattern provides a way to create objects without specifying the exact class of object that will be created. This is useful when the creation logic of a class is complex or when it needs to be decoupled from the user.

In React Functional Components: In React, the Factory Pattern can be used to dynamically create different components based on certain conditions or inputs. For example, you might have a component that needs to display different types of user interfaces depending on user type or preferences.

const UserAdminComponent = () => <div>Admin Interface</div>;
const UserGuestComponent = () => <div>Guest Interface</div>;
const UserMemberComponent = () => <div>Member Interface</div>;

const UserComponentFactory = ({ userType }) => {
    switch (userType) {
        case 'admin':
            return <UserAdminComponent />;
        case 'guest':
            return <UserGuestComponent />;
        case 'member':
            return <UserMemberComponent />;
        default:
            return <div>Invalid user type</div>;
    }
};

// Usage
<UserComponentFactory userType="admin" />
<UserComponentFactory userType="guest" />
<UserComponentFactory userType="member" />

Observer Pattern

The Observer Pattern is like subscribing to a magazine or newsletter. When you subscribe to a magazine, you receive new issues whenever they are published. You don't have to actively check for new issues; the magazine publisher notifies you when a new issue is available. Similarly, in programming, the Observer Pattern establishes a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.

In React Functional Components: In React, the Observer Pattern can be implemented using state and effect hooks to create components that observe changes in other components' states.

import React, { useState } from 'react';

const Subject = () => {
  const [data, setData] = useState('Initial Data');

  return (
    <div>
      <button onClick={() => setData('New Data')}>Change Data</button>
      <Observer data={data} />
    </div>
  );
};

const Observer = ({data }) => <div>Observed Data: {data}</div>;

// Usage
<Subject />;

In this example, Subject maintains the state of data, and Observer component observes changes in data. When the "Change Data" button is clicked in the Subject component, it updates the data state value. As a result, the Observer component automatically reflects the updated data without any explicit notification.

This pattern is particularly useful when you have components that need to react to changes in another component's state or data. It helps in creating loosely coupled components where they can react to changes without needing direct knowledge of how those changes occurred.

Implementing the Observer Pattern in React functional components allows for efficient management of component communication and ensures that UI updates are automatically triggered when relevant data changes occur

Mixin Pattern

A mixin is an object that we can use in order to add reusable functionality to another object or class, without using inheritance. We can’t use mixins on their own: their sole purpose is to add functionality to objects or classes without inheritance.

The Mixin Pattern can be implemented using object composition to combine the properties and methods of multiple objects into a single object. Here's an example of how you can achieve this:

// Define mixins as separate objects
const canEat = {
  eat: function() {
    console.log('Eating');
  }
};

const canSleep = {
  sleep: function() {
    console.log('Sleeping');
  }
};

// Create a target object
const animal = {};

// Apply mixins to the target object
Object.assign(animal, canEat, canSleep);

// Now 'animal' has both eat and sleep methods
animal.eat(); // Output: Eating
animal.sleep(); // Output: Sleeping
Copy

In this example, canEat and canSleep are two separate mixins containing the eat and sleep methods, respectively. By using Object.assign, these mixins are applied to the animal object, allowing it to exhibit combined behaviors.

Module Pattern

When Our application and codebase grow, it becomes increasingly important to keep your code maintainable and separated. The module pattern allows you to split up your code into smaller, reusable pieces.

Besides being able to split your code into smaller reusable pieces, modules allow you to keep certain values within your file private. Declarations within a module are scoped (encapsulated) to that module, by default. If we don’t explicitly export a certain value, that value is not available outside that module. This reduces the risk of name collisions for values declared in other parts of your codebase, since the values are not available on the global scale.

High Order Component

A Higher-Order Component (HOC) in React is a pattern where a function takes a component and returns a new component with additional functionality. It is used to share code between components, and it's a powerful pattern for code reuse, logic abstraction, and composition.

Example: Here's an example of a simple HOC that adds some additional props to the wrapped component:

import React from 'react';

const withExtraProps = (WrappedComponent) => {
  return (props) => {
    return <WrappedComponent {...props} extraProp="This is an extra prop" />;
  };
};

const DisplayComponent = (props) => {
  return (
    <div>
      <p>Regular Prop: {props.regularProp}</p>
      <p>Extra Prop: {props.extraProp}</p>
    </div>
  );
};

const EnhancedDisplayComponent = withExtraProps(DisplayComponent);

// Usage
<EnhancedDisplayComponent regularProp="Hello, World!" />
Copy

In this example, withExtraProps is a higher-order component that takes DisplayComponent as an argument and returns an enhanced version of it. The enhanced version includes an extra prop that is passed down to the original DisplayComponent.

By using HOCs, you can encapsulate common functionalities and apply them to multiple components without repeating the same code. This pattern promotes reusability and helps in keeping the components focused on their specific responsibilities.

Implementing HOCs in React allows for cleaner, more maintainable code by separating concerns and promoting code reuse across different parts of your application.

Compound Component

The compound component design pattern in React allows the creation of components that work together to form a cohesive unit. This pattern is used to compose components that share state and functionality, enabling a more intuitive and flexible API for the end user.

Example of Compound Components:

import React, { useState } from 'react';

const Toggle = ({ children }) => {
  const [on, setOn] = useState(false);

  const toggle = () => {
    setOn((prev) => !prev);
  };

  return React.Children.map(children, (child) => {
    if (typeof child.type === 'string') {
      return child;
    }

    return React.cloneElement(child, { on, toggle });
  });
};

const ToggleOn = ({ on, children }) => (on ? children : null);
const ToggleOff = ({ on, children }) => (on ? null : children);
const ToggleButton = ({ on, toggle }) => <button onClick={toggle}>{on ? 'ON' : 'OFF'}</button>;

// Usage
<Toggle>
  <ToggleOn>The button is on</ToggleOn>
  <ToggleOff>The button is off</ToggleOff>
  <ToggleButton />
</Toggle>;
Copy

In this example, Toggle acts as a container for other components (ToggleOn, ToggleOff, and ToggleButton). The state is managed at the Toggle level and shared with its child components. When the button is clicked, it toggles its state and updates the visibility of the ToggleOn and ToggleOff components accordingly.

By using compound components, you can create reusable and composable APIs that offer a more intuitive way for users to interact with your components. This pattern promotes encapsulation of related functionality within a single unit while providing flexibility for customization.


Rendering Patterns

  1. Client-Side Rendering

  2. Server-Side Rendering

  3. Static Side Rendering

  4. Progressive Hydrate

Client-Side Rendering

In CSR, JavaScript handles the rendering of your page in the browser. After the initial loading of the HTML and JavaScript, the React app takes over, manipulating the DOM as needed. This approach offers dynamic user experience but can lead to longer initial load times and lower SEO if not managed correctly.

Server-Side Rendering

In SSR, react components are rendered to HTML on the server, and the rendered HTML is sent to the client. This means the page is ready to be viewed as soon as it loads.

SSR enhances SEO and improves the initial load time, as the content is readily available for indexing and viewing. However, it can increase server load and complexity.

Image from Krusche Company

Also known as static site generation, this approach pre-renders pages during the build process. Each page is a pre-built static HTML file, often rehydrated into a React app on the client-side.Also known as static site generation, this approach pre-renders pages during the build process. Each page is a pre-built static HTML file, often rehydrated into a React app on the client-side.

It offers blazing-fast loading times and excellent SEO since pages are pre-rendered. However, it's not ideal for highly dynamic content. Best for websites with content that doesn't change often, like documentation or portfolio sites.

Progressive hydration

Progressive Hydration works as follows: Initially, when you open the website, the server provides a fully-rendered HTML page, allowing you to immediately see content like articles and images. This server-prepared content ensures quick loading and is easily indexed by search engines. As you interact with the page, such as clicking on an article or scrolling, React starts adding interactivity to this static content, a process known as 'hydration'. For instance, clicking an article might expand it or load the comments section, all happening client-side with React's help. This technique offers a fast initial load from the server-side rendering, coupled with a rich, interactive user experience provided by client-side React, balancing both speed and interactivity.

Progressive Hydration

Congratulations on successfully Reading my Article Now You have Basic understanding of Design Patten that how that work in React Ecosystem

I hope you learned something from this blog. If you have, don't forget to drop a like, follow me on Hashnode, and subscribe to my Hashnode newsletter so that you don't miss any future posts. If you have any questions or feedback, feel free to leave a comment below. Thanks for reading and have a great day!

If you want to Learn About Design Patten in Details You Can check here 👇

Patterns.dev