This article is published in English.
Understanding React Custom Hooks: Logic Reuse Without Shared State
Learn what React custom hooks are, how they extract and share stateful logic across components, and which common mistakes to avoid when building them.
Introduction
Have you ever found yourself writing the same useState and useEffect pairing across three different components, changing only the variable names each time? Almost every React developer runs into this at some point. The code works fine, but it means identical logic ends up duplicated across the codebase, and each copy tends to drift apart as it gets tweaked independently over time.
This is precisely the situation that custom hooks in React were designed to address. The React docs covering this topic explain that pulling component behavior into a dedicated, reusable function lets separate parts of an app share the same stateful behavior instead of copying the underlying implementation over and over. This part of the article walks through what custom hooks actually are, how they behave in real code, and the advantages and pitfalls you should understand before building your own.
What Are React Custom Hooks
At its core, a custom hook is nothing more than a JavaScript function that follows one strict naming rule: its name has to start with use. Inside, it can call other hooks like useState or useEffect, and it hands back whatever values or functions the calling component needs. React itself doesn't give this function any special runtime treatment — the use prefix is purely a convention, but it's an essential one, since it lets React correctly enforce the rules of hooks and lets other developers instantly recognize what the function is for.
Custom Hooks vs Regular Functions
A standard utility function can do plenty of useful work, but it's barred from calling other hooks because it sits outside React's rendering machinery. A custom hook doesn't have that restriction. That's precisely its advantage: you can wrap up stateful behavior — tracking a loading flag, listening for a browser event, or anything similar — into something reusable that still plugs cleanly into React's component lifecycle.
How React Custom Hooks Work in Practice
A concrete example clarifies this far faster than any abstract explanation.
Extracting Logic Out of a Component
Imagine three unrelated components that each need to detect whether the browser currently has a network connection. Without a shared hook, every one of them would need to build its own useState and useEffect setup to watch for that:
function StatusBadge() {
const [isOnline, setIsOnline] = useState(navigator.onLine);
useEffect(() => {
const goOnline = () => setIsOnline(true);
const goOffline = () => setIsOnline(false);
window.addEventListener('online', goOnline);
window.addEventListener('offline', goOffline);
return () => {
window.removeEventListener('online', goOnline);
window.removeEventListener('offline', goOffline);
};
}, []);
return <span>{isOnline ? 'Online' : 'Offline'}</span>;
}
Instead, you can lift that logic into a single custom hook, and from then on any component that needs the status simply calls it:
function useOnlineStatus() {
const [isOnline, setIsOnline] = useState(navigator.onLine);
useEffect(() => {
const goOnline = () => setIsOnline(true);
const goOffline = () => setIsOnline(false);
window.addEventListener('online', goOnline);
window.addEventListener('offline', goOffline);
return () => {
window.removeEventListener('online', goOnline);
window.removeEventListener('offline', goOffline);
};
}, []);
return isOnline;
}
// Usage in any component:
function StatusBadge() {
const isOnline = useOnlineStatus();
return <span>{isOnline ? 'Online' : 'Offline'}</span>;
}
None of the components has to wire up the event listeners on its own anymore — each just asks useOnlineStatus for the current value.
Sharing State Logic Without Sharing State
This is a detail that catches a lot of newcomers off guard: a custom hook shares behavior, not state itself. If two separate components each invoke useOnlineStatus, each one gets its own private copy of that state. They run through identical logic, but the actual values living in each component are completely independent — there's no shared or linked state between them.
Key Benefits of Using React Custom Hooks
- Reusability across components: Once the logic lives inside a hook, no component in the application needs to duplicate a single line to use it, and you get a guarantee that the behavior stays consistent everywhere it's used.
- Cleaner, more readable components: Pulling stateful logic out of a component means the remaining component code is largely free of state management concerns, making it much faster for someone new to understand at a glance.
- Easier testing in isolation: The extracted logic can be tested completely separately from any component that consumes it, meaning it only has to be verified once rather than being retested indirectly every time a consuming component changes.
- Access to experienced talent: Building hooks that are truly reusable — rather than just renamed, copy-pasted logic with a
useprefix bolted on — demands genuine familiarity with React's underlying patterns. Teams that want this handled properly from the outset sometimes look outside their own staff and bring in dedicated ReactJS developers, specialists whose experience helps hook design remain solid even as the project expands.
Common Mistakes to Avoid
Even hooks written with good intentions can go wrong in a handful of predictable ways.
- Violating the rules of hooks: Invoking a hook inside a conditional, within a loop, or from a plain function rather than a component or another hook interferes with React's ability to keep state consistent across renders. The result is typically an odd, intermittent bug that's difficult to trace back to its cause.
- Piling too much into one hook: Stuffing form validation, network requests, and UI state management into a single custom hook makes that hook harder to test, harder to reuse, and harder to reason about than if the same responsibilities were split across two or three smaller, more focused hooks.
- Extracting logic that didn't need extracting: Not every
useStatecall deserves to become its own hook. If a piece of logic is only ever used in one spot, pulling it out just adds an extra layer of indirection without any real payoff. - Forgetting to memoize when it counts: When a custom hook returns functions or objects without memoizing them properly, components consuming that hook can end up re-rendering unnecessarily. This kind of performance cost is subtle and easy to overlook during routine testing.
Conclusion
Custom hooks in React do more than help you avoid repeating code, they act as a real mechanism for keeping an expanding codebase coherent and consistent. Once a team develops an instinct for spotting logic worth pulling out, components tend to shrink in complexity, bugs become easier to track down, and behavior stays uniform across the app rather than slowly diverging between components that were supposed to work the same way. As React applications continue to grow in scale and complexity, understanding when and how to build a solid custom hook is turning into a baseline skill rather than something reserved for advanced developers.
If your team is navigating a codebase riddled with duplicated logic and wants to bring real structure to it, it may be worth consulting a React js development company that has already guided similar refactors on other large-scale projects.