0Pricing
React Academy · Lesson

Refactoring a HOC to a Custom Hook

Convert a withAuth HOC into a useAuth hook step-by-step.

Refactoring a HOC to a Custom Hook is a free React Academy lesson on CoddyKit — lesson 4 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the React Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

Welcome

In this lesson you will refactor a withAuth HOC into a useAuth custom hook step-by-step, seeing exactly why the hook version is cleaner and easier to test.

The withAuth HOC (Starting Point)

The HOC takes a component, checks if the user is authenticated, and redirects to /login if not. It injects the user as a prop.
function withAuth(Component) {
  return function AuthGuard(props) {
    const { user, loading } = useAuthState();
    if (loading) return <Spinner />;
    if (!user) return <Navigate to="/login" />;
    return <Component {...props} user={user} />;
  };
}

const ProtectedPage = withAuth(MyPage);

Problems with the HOC

The HOC: (1) adds a wrapper in DevTools, (2) implicitly injects `user` prop — the component must know to expect it, (3) is hard to test without rendering the full HOC wrapper, (4) is not composable with other hooks easily.

Step 1: Extract Logic into a Hook

Move all the logic from the HOC into a custom hook. The hook returns the auth state directly — no wrapping, no prop injection.
function useAuth() {
  const { user, loading } = useAuthState();
  return { user, loading, isAuthenticated: !!user };
}

Step 2: Handle Redirects in the Hook

Move the redirect logic into a separate hook (e.g. useRequireAuth) that calls navigate internally and returns the user.
function useRequireAuth(redirectTo = '/login') {
  const { user, loading } = useAuth();
  const navigate = useNavigate();

  useEffect(() => {
    if (!loading && !user) navigate(redirectTo);
  }, [user, loading, navigate, redirectTo]);

  return { user, loading };
}

Step 3: Update the Component

Replace the HOC wrapper with a direct hook call inside the component. The component explicitly calls useRequireAuth and handles loading. Its dependency is now visible.
function MyPage() {
  const { user, loading } = useRequireAuth();
  if (loading) return <Spinner />;
  return <p>Hello, {user.name}</p>;
}

Benefits Compared

The hook version: (1) no extra wrapper in DevTools, (2) explicit dependency — you see `useRequireAuth` in the component, (3) testable by mocking the hook, (4) composable with other hooks in the same component.

Testing the Hook Version

Mock useRequireAuth in your test by replacing its module export. The component test only needs to render the component and assert on the output — no HOC setup required.
vi.mock('./useRequireAuth', () => ({
  useRequireAuth: () => ({ user: mockUser, loading: false })
}));

render(<MyPage />);
expect(screen.getByText('Hello, Alice')).toBeInTheDocument();

When to Keep a HOC

Keep the HOC if you need to protect many components declaratively (e.g. in route definitions) and do not want to add the hook call to each one. You can even build the HOC ON TOP of the hook.
function withAuth(Component) {
  return function (props) {
    const { user, loading } = useRequireAuth();
    if (loading) return <Spinner />;
    return <Component {...props} user={user} />;
  };
}

The Best of Both Worlds

The final architecture: a useRequireAuth hook for direct use in components, AND a thin withAuth HOC built on top of it for declarative route protection. Both share the same hook logic.

Quick Check

What is the main benefit of calling useRequireAuth inside a component instead of wrapping it with a withAuth HOC?

Recap

Refactor HOCs to hooks by extracting logic into a custom hook, moving side effects (like redirects) into the hook, and calling the hook directly in the component. Build a thin HOC on top of the hook when declarative wrapping is needed.

Course Complete

Congratulations! You finished **React Patterns: Render Props & HOCs**. You now understand both classic patterns, their trade-offs, and how to refactor them to modern custom hooks.

Frequently asked questions

Is the “Refactoring a HOC to a Custom Hook” lesson free?

Yes — the full text of “Refactoring a HOC to a Custom Hook” is free to read here on the web, and the React Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the React Academy course, upgrade to CoddyKit PRO.

What will I learn in “Refactoring a HOC to a Custom Hook”?

Convert a withAuth HOC into a useAuth hook step-by-step. You practise React Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start React Academy?

No prior experience is required. React Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 4 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “Refactoring a HOC to a Custom Hook” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this React Academy lesson?

Yes. Every React Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. Render Props Pattern
  2. Higher-Order Components (HOCs) Explained
  3. HOC vs Render Props vs Custom Hooks
  4. Refactoring a HOC to a Custom Hook
← Back to React Academy