obah sylva

Custom Hooks in React: Reusing Logic the Right Way

Share:fXinW
Share this article

The React Compiler’s stable release in October 2025 removed one of the biggest reasons developers reached for custom hooks in the first place — wrapping repeated `useMemo`/`useCallback` boilerplate into a shared function. That’s actually clarified what custom hooks are genuinely for. With automatic memoization now handled by the compiler, custom hooks in 2026 are almost entirely about one thing: extracting and sharing stateful logic, not performance workarounds. That narrower, sharper definition is the right lens for using them well.

What a custom hook actually is

A custom hook is just a JavaScript function whose name starts with `use` and that calls other hooks inside it. That’s the entire definition — there’s no special syntax, no separate API. If a piece of logic in a component involves `useState`, `useEffect`, `useRef`, or any other hook, and that same logic needs to exist in more than one component, pulling it into a `use`-prefixed function is a custom hook. React’s linter rules recognize the naming convention and enforce the Rules of Hooks against it the same way they do for built-in hooks.

The problem custom hooks solve

Before hooks existed, sharing stateful logic between components meant reaching for patterns like higher-order components or render props, both of which added extra wrapper components to the tree and made the resulting code harder to trace. Custom hooks solve the same problem without any of that extra nesting — the logic runs inside the calling component itself, at the same tree depth, with no wrapper. Two completely unrelated components can both call `useWindowWidth()` or `useDebouncedValue()` and each gets its own independent, isolated instance of that state; nothing is shared between them except the logic itself.

What belongs in a hook versus a plain function

This is the most common point of confusion. If the logic doesn’t call any other hooks internally, it doesn’t need to be a hook at all — a plain utility function is simpler, easier to test, and doesn’t carry the Rules of Hooks restrictions (no conditional calls, no calls inside loops, must be called at the top level of a component). A function that formats a date or sums an array should stay a plain function. A function that needs `useState` to track a value over time, or `useEffect` to synchronize with something outside React, is a genuine candidate for a hook.

A practical example

A common real-world case: several components each need to know whether the user is online. Writing `useEffect` with `navigator.onLine` and event listeners for `online`/`offline` in every one of those components duplicates the same setup and cleanup code repeatedly, and duplicated cleanup logic is exactly where subtle bugs creep in. Extracting it into a single `useOnlineStatus()` hook — internally just a `useState` plus a `useEffect` that adds and removes the event listeners — means every component that needs the value just calls the hook and gets a boolean back, with the setup and teardown logic written and tested in exactly one place.

Common mistakes to avoid

  • Over-abstracting too early. A hook that’s used in exactly one component isn’t providing reuse value yet — it’s often clearer to leave the logic inline until a second, genuinely separate use case appears.
  • Hiding too much inside one hook. A hook that returns eight different values and manages three unrelated concerns is hard to reason about. Smaller, single-purpose hooks compose better than one large one.
  • Forgetting cleanup. Any hook that sets up a subscription, timer, or event listener needs a cleanup function returned from its `useEffect`, or the same bug gets duplicated across every component that uses the hook, instead of fixed once.
  • Reaching for a hook when compiler-era memoization would do. With the React Compiler now stable and handling automatic memoization, a custom hook whose only job used to be wrapping `useMemo` for performance reasons is largely unnecessary in a compiler-enabled 2026 codebase.

The takeaway

Custom hooks in 2026 have a cleaner job description than they did a few years ago: they exist to share genuinely stateful logic across components without wrapper components or duplicated setup and cleanup code — not to work around performance problems the compiler now handles automatically. Used for that one job, at the right size, they’re one of the cleanest patterns React offers for keeping component logic reusable and easy to reason about.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top