Chapter 41: Reusing Logic with Custom Hooks
Core Idea
A custom Hook is just a JavaScript function whose name starts with use and that calls other Hooks inside it — it lets stateful logic (not just the resulting JSX) be extracted and reused across components, which neither a regular function nor a wrapper component can do on its own.
Key Concepts
- Why not just a regular function? A plain helper function can share calculations, but not stateful behavior — it can't call
useState/useEffect itself and have that state live inside the calling component the way a custom Hook can.
- Naming and rules: must start with
use; internally calls other Hooks (built-in or custom), and therefore must itself follow the Rules of Hooks (Ch 116) — called only at the top level, not inside conditions/loops.
- What gets shared is the logic, not the state itself. Two components each calling the same custom Hook (
useOnlineStatus()) get two fully independent instances of that Hook's internal state — exactly like two calls to useState in two different components never share a value. The Hook shares behavior, not a value.
- Passing reactive values between Hooks: a custom Hook can accept parameters and return values just like a regular function, and those parameters flow into the Hooks it calls internally — e.g. a
useChatRoom({ serverUrl, roomId }) custom Hook wraps a useEffect whose dependency array includes serverUrl/roomId, so the parameters passed to the custom Hook drive the Effect the same way they would if written inline.
- When to extract a custom Hook: when the same stateful logic (not just markup) is duplicated across multiple components, or when isolating a piece of synchronization logic makes a component's own code substantially more readable — even for logic used only once, if it clarifies intent (a
useChatRoom name documents "this manages a chat connection" better than an inline Effect does).
- Custom Hooks let implementation details change without touching call sites — swapping an underlying browser API a Hook wraps (e.g. how "online status" is detected) only requires editing the Hook's internals; every component using
useOnlineStatus() is unaffected.
Code Examples
function useOnlineStatus() {
const [isOnline, setIsOnline] = useState(navigator.onLine);
useEffect(() => {
function update() { setIsOnline(navigator.onLine); }
window.addEventListener('online', update);
window.addEventListener('offline', update);
return () => {
window.removeEventListener('online', update);
window.removeEventListener('offline', update);
};
}, []);
return isOnline;
}
- What it demonstrates: a complete custom Hook — internally uses
useState + useEffect, exposes a single derived value, reusable by any component as const isOnline = useOnlineStatus();.
Key Takeaways
- Custom Hooks share reusable stateful logic; each call site still gets its own independent state — never assume two components using the same custom Hook are somehow "connected."
- Extracting a custom Hook is often worth it purely for naming/readability, even without duplication — a well-named Hook documents what an Effect is for.
- Because a custom Hook is "just a function that calls Hooks," everything from earlier chapters (dependency arrays, Effect Events, purity) still applies inside it exactly as it would inline.
Connects To
- Ch 36 (Synchronizing with Effects): the Effect logic most custom Hooks in this style wrap.
- Ch 116 (Rules of Hooks): the rules a custom Hook must follow, same as any built-in Hook usage.
- Ch 12 (Importing and Exporting Components): the same export/import mechanics apply to a custom Hook's own file.