Writing Custom Hooks
Extracting stateful logic into reusable functions, and the rules of hooks.
What you'll learn
- What makes a function a hook
- Extracting repeated stateful logic without a wrapper component
- The two rules of hooks, and the reason behind them
- What is shared between components using the same hook — and what is not
A hook is just a function
A custom hook is a function whose name starts with use and which calls other
hooks. There is no API to register, no special import. The naming convention is
what tells React — and the linter — to apply the rules of hooks to it.
Here is a component with a small amount of logic worth naming:
Two independent pieces of state, one shared piece of logic.
State is not shared
This is the part people expect to work the other way round. A custom hook shares
logic, not state. Every component that calls the hook gets its own state,
exactly as if it had called useState directly.
The two counters below use the same hook and move independently:
To genuinely share one value between components, you need state in a common parent or useContext.
A hook with an effect
Hooks come into their own when they bundle an effect and its cleanup, so callers cannot forget the cleanup half. Resize the preview pane:
The component reads like a description of what it needs — a width — with the listener, the state, and the teardown all named and hidden.
The rules of hooks
There are two, and they follow from one implementation detail: React tracks
hooks by call order, not by name. The first useState in a component is
matched with the first useState from the previous render, and so on.
1. Only call hooks at the top level. Not inside conditions, loops, or nested functions:
// Broken: on renders where isLoggedIn is false, the hook order changes,
// and React hands the wrong state to the wrong call.
if (isLoggedIn) {
const [name, setName] = useState("");
}
// Fine: the condition goes inside.
const [name, setName] = useState("");
if (isLoggedIn) {
// ...
}
2. Only call hooks from React functions. From components, or from other hooks — not from ordinary functions, class methods, or event handlers.
The use prefix is what lets tooling check this for you. Name a hook
getToggle and the linter will not know it contains hooks.
What belongs in a hook
A good custom hook has a job you can name: useToggle, useWindowWidth,
useFetch, useLocalStorage. If you cannot name it without "and", it is
probably two hooks.
Do not extract just to shorten a component. Extract when logic is duplicated, when a component is doing several unrelated things at once, or when a subscribe and unsubscribe pair should always travel together.
Common mistakes
- Not starting the name with
use. The rules stop being enforced. - Expecting shared state. Each caller gets its own.
- Calling a hook conditionally. Move the condition inside the hook.
- Extracting too early. One use with no duplication rarely earns a hook.
Exercise
App has the same show/hide logic written twice. Extract it into a
useToggle hook in /useToggle.js and use it for both panels.