Understanding High Order Components with Examples

DikshaDiksha
3 min read

Higher-Order Components (HOCs) are a pattern in React for reusing component logic. An HOC is a function that takes a component and returns a new component with additional props or functionality.

Key Points about HOCs:

  1. HOCs are Pure Functions: They do not modify the original component, but rather create a new one with enhanced capabilities.

  2. Composition: HOCs help in composing components by adding behavior or props to them.

  3. Reusability: They enable reusing component logic in different parts of an application without duplicating code.

Example:

Let's say we have a component that displays a list of users. We want to add the ability to fetch data from an API and handle loading and error states. We can create an HOC to handle the data fetching logic.

Step 1: Create a Basic Component

First, we create a simple UserList component that displays a list of users.

// UserList.js
import React from 'react';

const UserList = ({ data }) => {
  return (
    <ul>
      {data.map(user => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
};

export default UserList;

Step 2: Create the HOC

Next, we create an HOC called withDataFetching that fetches data from an API and handles loading and error states.

// withDataFetching.js
import React, { useEffect, useState } from 'react';

const withDataFetching = (url) => (WrappedComponent) => {
  return (props) => {
    const [data, setData] = useState([]);
    const [loading, setLoading] = useState(true);
    const [error, setError] = useState(null);

    useEffect(() => {
      const fetchData = async () => {
        try {
          const response = await fetch(url);
          const result = await response.json();
          setData(result);
        } catch (error) {
          setError(error);
        } finally {
          setLoading(false);
        }
      };

      fetchData();
    }, [url]);

    if (loading) return <div>Loading...</div>;
    if (error) return <div>Error: {error.message}</div>;

    return <WrappedComponent data={data} {...props} />;
  };
};

export default withDataFetching;

Step 3: Wrap the Component with the HOC

Finally, we use the withDataFetching HOC to enhance the UserList component with data fetching logic.

// App.js
import React from 'react';
import UserList from './UserList';
import withDataFetching from './withDataFetching';

const UserListWithFetching = withDataFetching('https://jsonplaceholder.typicode.com/users')(UserList);

const App = () => {
  return (
    <div>
      <h1>Users</h1>
      <UserListWithFetching />
    </div>
  );
};

export default App;

In this example:

  • UserList is the base component that displays a list of users.

  • withDataFetching is the HOC that adds data fetching logic to any component.

  • UserListWithFetching is the enhanced component that fetches user data from the given API and displays it using UserList.

Benefits of HOCs:

  • Separation of Concerns: Logic for fetching data, handling loading states, and displaying data is separated.

  • Reusability: The withDataFetching HOC can be used with any component that needs data fetching.

Here are a few more common use cases:

1. Authentication : Ensuring that only authenticated users can access certain parts of your application is a common requirement.

// withAuth.js
import React from 'react';
import { Redirect } from 'react-router-dom';

const withAuth = (WrappedComponent) => {
  return (props) => {
    const isAuthenticated = // logic to check if user is authenticated
    if (!isAuthenticated) {
      return <Redirect to="/login" />;
    }
    return <WrappedComponent {...props} />;
  };
};

export default withAuth;

Usage:

// SomeProtectedComponent.js
import React from 'react';
import withAuth from './withAuth';

const Dashboard = () => {
  return <div>Dashboard</div>;
};

export default withAuth(Dashboard);

2. Logging: You might want to log certain actions or component renders for analytics or debugging purposes.

// withLogging.js
import React, { useEffect } from 'react';

const withLogging = (WrappedComponent) => {
  return (props) => {
    useEffect(() => {
      console.log(`Component ${WrappedComponent.name} mounted`);
      return () => {
        console.log(`Component ${WrappedComponent.name} unmounted`);
      };
    }, []);

    return <WrappedComponent {...props} />;
  };
};

export default withLogging;

Usage:

// SomeComponent.js
import React from 'react';
import withLogging from './withLogging';

const Profile = () => {
  return <div>Profile</div>;
};

export default withLogging(Profile);
0
Subscribe to my newsletter

Read articles from Diksha directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Diksha
Diksha