How to Implement Higher-Order Components in React

Higher-Order Components (HOCs) are an advanced pattern in React used for reusing component logic across your application. This article provides a straightforward guide on what HOCs are, why they are useful, and a step-by-step walkthrough of how to implement them with a practical code example.

What is a Higher-Order Component?

A Higher-Order Component is not a part of the React API, but rather a pattern that emerges from React’s compositional nature. Professionally speaking, an HOC is a pure function that takes a component as an argument and returns a new, enhanced component.

const EnhancedComponent = higherOrderComponent(WrappedComponent);

HOCs are commonly used for cross-cutting concerns such as authorization, logging, data fetching, or applying specific styling.

Step-by-Step Implementation of an HOC

To implement an HOC, you need to create a function that accepts a component, defines a container component inside, passes the necessary props, and returns that container component.

Here is an implementation of a withLoading HOC, which displays a loading spinner instead of the wrapped component if the data is still loading.

1. Define the HOC Function

Create a function that takes a component as its argument. Inside this function, return a new functional component.

import React from 'react';

// The HOC function
function withLoading(WrappedComponent) {
  return function WithLoadingComponent({ isLoading, ...otherProps }) {
    if (isLoading) {
      return <div>Loading, please wait...</div>;
    }
    // Pass all other props down to the wrapped component
    return <WrappedComponent {...otherProps} />;
  };
}

export default withLoading;

2. Prepare the Wrapped Component

This is the standard component that you want to enhance with the loading logic.

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

function UserProfile({ username, email }) {
  return (
    <div>
      <h2>User Profile</h2>
      <p>Username: {username}</p>
      <p>Email: {email}</p>
    </div>
  );
}

export default UserProfile;

3. Wrap the Component

To apply the HOC, import both the original component and the HOC, then pass the component to the HOC function.

// App.js
import React, { useState, useEffect } from 'react';
import UserProfile from './UserProfile';
import withLoading from './withLoading';

// Create the enhanced component
const UserProfileWithLoading = withLoading(UserProfile);

function App() {
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    // Simulate API fetch delay
    const timer = setTimeout(() => setLoading(false), 2000);
    return () => clearTimeout(timer);
  }, []);

  return (
    <div className="App">
      <UserProfileWithLoading 
        isLoading={loading} 
        username="JohnDoe" 
        email="john@example.com" 
      />
    </div>
  );
}

export default App;

Best Practices for Using HOCs