DEV Community

Cover image for Custom Hooks vs. Higher-Order Components (HOCs) in React: A Practical Guide for Developers
Gouranga Das Samrat
Gouranga Das Samrat

Posted on

Custom Hooks vs. Higher-Order Components (HOCs) in React: A Practical Guide for Developers

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;
Enter fullscreen mode Exit fullscreen mode

const Dashboard = ({ user }) => {
  return <h1>Welcome, {user.name}!</h1>;
};
export default withAuth(Dashboard);
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

import useAuth from '../hooks/useAuth';
const Dashboard = () => {
  const isAuthenticated = useAuth();
  if (!isAuthenticated) {
    return null;
  }
  return <h1>Welcome to your Dashboard!</h1>;
};
export default Dashboard;
Enter fullscreen mode Exit fullscreen mode

The component explicitly calls the hook to get the authentication status.

Head-to-Head Comparison: The Real Impact

captionless image

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)