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
- Pass Unrelated Props Through: Always ensure that
your HOC passes all unrelated props through to the wrapped component
using the spread operator (
{...otherProps}). This ensures the wrapped component works seamlessly. - Do Not Mutate the Original Component: HOCs should use composition rather than mutation. Modifying a component’s prototype inside an HOC can lead to conflicts and unexpected bugs.
- Set a Display Name for Debugging: To make debugging
easier in React Developer Tools, set the
displayNameof the returned component to reflect the HOC wrapper (e.g.,WithLoading(UserProfile)).