Higher-Order Components
Master Higher-Order Components (HOCs) to create reusable logic and enhance components with cross-cutting concerns.
90 minā¢By Priygop Teamā¢Last updated: Feb 2026
Understanding HOCs
Higher-Order Components are functions that take a component and return a new component with additional functionality. They are a powerful pattern for code reuse and logic sharing in React applications.
Basic HOC Implementation
Example
// Basic HOC example
function withLoading(WrappedComponent) {
return function WithLoadingComponent({ isLoading, ...props }) {
if (isLoading) {
return <div>Loading...</div>;
}
return <WrappedComponent {...props} />;
};
}
// Usage
const UserProfile = ({ user }) => <div>{user.name}</div>;
const UserProfileWithLoading = withLoading(UserProfile);Advanced HOC Patterns
Example
// HOC with state and lifecycle
function withData(WrappedComponent) {
return class extends React.Component {
constructor(props) {
super(props);
this.state = { data: null, loading: true };
}
componentDidMount() {
this.fetchData();
}
fetchData = async () => {
try {
const data = await api.getData();
this.setState({ data, loading: false });
} catch (error) {
this.setState({ error, loading: false });
}
}
render() {
return <WrappedComponent {...this.props} {...this.state} />;
}
};
}HOC Best Practices
- Don't mutate the original component
- Pass through unrelated props
- Maximize composability
- Display name for debugging
- Don't use HOCs inside render method
- Copy static methods if needed
Common HOC Examples
Example
// Authentication HOC
const withAuth = (WrappedComponent) => {
return function AuthenticatedComponent(props) {
const { user, isAuthenticated } = useAuth();
if (!isAuthenticated) {
return <LoginPage />;
}
return <WrappedComponent {...props} user={user} />;
};
};
// Error Boundary HOC
const withErrorBoundary = (WrappedComponent) => {
return class extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
console.error('Error caught by HOC:', error, errorInfo);
}
render() {
if (this.state.hasError) {
return <div>Something went wrong.</div>;
}
return <WrappedComponent {...this.props} />;
}
};
};Mini-Project: Data Fetching HOC
Example
// Complete data fetching HOC with caching
import React, { useState, useEffect, useCallback } from 'react';
const withDataFetching = (url, options = {}) => (WrappedComponent) => {
return function WithDataFetching(props) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const [refetch, setRefetch] = useState(0);
const fetchData = useCallback(async () => {
try {
setLoading(true);
setError(null);
const response = await fetch(url, options);
const result = await response.json();
setData(result);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
}, [url, JSON.stringify(options)]);
useEffect(() => {
fetchData();
}, [fetchData, refetch]);
const handleRefetch = () => setRefetch(prev => prev + 1);
return (
<WrappedComponent
{...props}
data={data}
loading={loading}
error={error}
refetch={handleRefetch}
/>
);
};
};
// Usage example
const UserList = ({ data, loading, error, refetch }) => {
if (loading) return <div>Loading users...</div>;
if (error) return <div>Error: {error}</div>;
return (
<div>
<button onClick={refetch}>Refresh</button>
<ul>
{data?.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
</div>
);
};
const UserListWithData = withDataFetching('/api/users')(UserList);
export default UserListWithData;