If you’ve been in the React ecosystem for a while, you’ve likely faced a common dilemma: you have logic that needs to be shared across multiple components. How do you do it effectively without creating a mess?
For years, the go-to solution was the Higher-Order Component (HOC) pattern. Then, in 2018, React 16.8 introduced Hooks, and with them, a new powerful way to share logic: Custom Hooks.
This often leads to confusion. Which one should you use? Is one objectively better? Let’s break down both patterns, compare them with practical examples, and see the real-world impact of each choice on your codebase.
1. What is a Higher-Order Component (HOC)?
A Higher-Order Component is an advanced pattern where a function takes a component and returns a new, enhanced component. It’s a direct application of the concept of component composition.
Think of it like a wrapper. The HOC provides additional props or behavior to the wrapped (“enhanced”) component.
Example: A HOC for Authentication
Let’s say we need to protect a component from users who aren’t logged in.
// withAuth.js (The HOC)
import { useEffect } from 'react';
import { useRouter } from 'next/router'; // or useNavigate from React Router
const withAuth = (WrappedComponent) => {
const RequiresAuth = (props) => {
const router = useRouter();
const isAuthenticated = checkAuth(); // Your auth logic
useEffect(() => {
if (!isAuthenticated) {
router.push('/login');
}
}, [isAuthenticated, router]);
return isAuthenticated ? <WrappedComponent {...props} /> : null;
};
return RequiresAuth;
};
export default withAuth;
const Dashboard = ({ user }) => {
return <h1>Welcome, {user.name}!</h1>;
};
export default withAuth(Dashboard);
The component _Dashboard_ now receives the _user_ prop from the HOC and is protected.
2. What is a Custom Hook?
A Custom Hook is a JavaScript function whose name starts with “use” and that can call other Hooks. It’s not a component; it’s a mechanism to reuse stateful logic.
You extract component logic into a reusable function. Unlike HOCs, custom hooks don’t modify the component itself; they just provide data and functions to it.
Example: A Custom Hook for Authentication
Let’s solve the same authentication problem with a custom hook.
// useAuth.js (The Custom Hook)
import { useEffect, useState } from 'react';
import { useRouter } from 'next/router';
const useAuth = (redirectTo = '/login') => {
const [isAuthenticated, setIsAuthenticated] = useState(false);
const router = useRouter();
useEffect(() => {
const authStatus = checkAuth(); // Your auth logic
setIsAuthenticated(authStatus);
if (!authStatus && redirectTo) {
router.push(redirectTo);
}
}, [redirectTo, router]);
return isAuthenticated;
};
export default useAuth;
import useAuth from '../hooks/useAuth';
const Dashboard = () => {
const isAuthenticated = useAuth();
if (!isAuthenticated) {
return null;
}
return <h1>Welcome to your Dashboard!</h1>;
};
export default Dashboard;
The component explicitly calls the hook to get the authentication status.
Head-to-Head Comparison: The Real Impact
So, Which One Should You Use? The Verdict
The React team has made it clear: Custom Hooks are the recommended way to share logic in most cases.
Use Custom Hooks when:
- You are dealing with stateful logic (like tracking form fields, managing subscriptions, fetching data).
- You want to keep your component tree clean and easy to debug.
- You need to reuse the same logic in completely different types of components (e.g., a form and a button).
Consider HOCs (sparingly) when:
- You need to add specific lifecycle methods or behavior that can’t be handled inside a hook.
- You are using a library that hasn’t yet migrated to a hooks-based API (e.g., some older React-Redux or React Router code).
- You want to inject props in a way that’s decoupled from the component’s implementation.
If you found this breakdown helpful, give it a clap!

Top comments (0)