what is a custom hook?
A custom hook is a plain JavaScript function whose name starts with
use and that may call other hooks. It extracts the wiring of state +
effects + refs into one reusable unit, so useState + useEffect +
useRef boilerplate never has to be copy-pasted between components. The hook itself
holds no state — each component that calls it gets its own isolated
state. That is the whole point: shared logic, private state.
| rule | what it means | why it matters |
|---|---|---|
1. name starts with use |
e.g. useToggle, useLocalStorage — capital U, then lowercase |
the linter (eslint-plugin-react-hooks) & Fast Refresh key off this prefix; without it they cannot detect hook violations |
| 2. may call other hooks | useState, useEffect, useReducer, even other custom hooks, freely inside |
this is what makes the logic "stateful" — composing hooks is composition of state machines |
| 3. obeys the Rules of Hooks | call hooks at the top level, never inside loops/conditions/nested functions | React tracks hook state by call order — conditional calls break that bookkeeping |
| 4. returns anything | value, [value, setter] array, or {key, key} object — your choice | convention: arrays for a single concept (caller renames), objects for many fields |
| 5. takes anything | initial values, callbacks, refs — pure arguments in, anything out | hooks are just functions; params let callers configure behavior at the call site |
1 · three hooks you write (edit me)
2 · Babel compiles JSX → element tree
<CustomHooksDemo/> becomes React.createElement(CustomHooksDemo).
The three use* functions are ordinary JS — only their calls to React hooks
are special. Each component instance owns a separate slot of state per hook call.
// (hit "compile & render" to see Babel's output)
3 · live React (three hooks, proven)
Click Flip to toggle useToggle, click + to drive useCounter
while usePrevious trails the last value. The gold-check
does this automatically: assert OFF → flip ON → flip OFF → inc (1/0) → inc (2/1) → reset (0/2).
live: — · gold: OFF → ON → OFF → 1/0 → 2/1 → 0/2
common custom hooks catalog
| hook | wraps | common use case |
|---|---|---|
useToggle(initial) |
useState |
boolean on/off flags — modals, switches, accordions |
usePrevious(value) |
useRef + useEffect |
access the previous value of any state/prop (diff detection) |
useCounter(initial) |
useState + useCallback |
increment / decrement / reset numeric state with stable callbacks |
useDebounce(value, ms) |
useState + useEffect |
delay a rapidly-changing value (search-as-you-type, resize) |
useLocalStorage(key, init) |
useState + useEffect |
persist state across reloads; lazy-init from localStorage |
useFetch(url) |
useState + useEffect |
data fetching with {data, loading, error} shape |
useMediaQuery(query) |
useState + useEffect |
subscribe to CSS media queries, re-render on breakpoint change |
useEventListener(target, event, fn) |
useRef + useEffect |
attach/detach DOM or window listeners with correct cleanup |