| useState |
useState(initial) |
[state, setState] |
A setState with a value Object.is says
is new |
Reading state right after setting
it |
2.3.1 |
| useState (lazy) |
useState(() => make()) |
same |
Initializer runs on the first render
only |
Passing make(), which runs every
render |
2.3.5 |
| useState (updater) |
setState(prev => next) |
same |
Queues against the latest value, not
this render's |
Batching repeated setState(count + 1)
calls |
2.3.3 |
| useReducer |
useReducer(fn, arg, init?) |
[state, dispatch] |
A dispatch whose reducer returns a new
object |
Mutating state inside the reducer |
2.5.6 |
| useReducer (lazy) |
useReducer(fn, arg, init) |
same |
init(arg) runs on the first render
only |
Rebuilding the initial state on every
render |
2.5.7 |
| useContext |
useContext(Ctx) |
Nearest provider's value |
The provider's value changes
identity |
A fresh object literal as value each
render |
2.5.2 |
| useRef |
useRef(initial) |
{ current }, stable |
Never; writing current re-renders
nothing |
Reading current during render |
2.6.1 |
| useRef (callback) |
ref={node => cleanup} |
React 19 runs the returned cleanup |
On attach and again on detach |
Returning anything but a cleanup
function |
2.6.4 |
| useImperativeHandle |
useImperativeHandle(ref, create,
deps?) |
Nothing |
deps change, after DOM commit |
Exposing the node instead of a small
API |
2.6.8 |
| Table B.1: State,
context and ref hooks |
Effects run after React has committed to the DOM. Which one you pick
decides where in that commit you land: after the browser paints, before
it paints, or before the styles are even inserted.
| useEffect |
useEffect(setup, deps?) |
Nothing |
After paint, when a dep changes
identity |
Omitting the cleanup, so subscriptions
stack up |
2.4.1 |
| useEffect (deps) |
deps omitted, [], or listed |
Nothing |
Every render, once, or on change |
Silencing the lint rule instead of
fixing deps |
2.4.2 |
| useEffect (cleanup) |
return () => stop() |
Nothing |
Before the next setup and on
unmount |
Letting a slow response overwrite a
newer one |
2.4.5 |
| useLayoutEffect |
useLayoutEffect(setup, deps?) |
Nothing |
Before paint, blocking it |
Measuring layout work that useEffect
could do |
2.4.8 |
| useInsertionEffect |
useInsertionEffect(setup, deps?) |
Nothing |
Before any DOM mutation |
Using it outside a CSS-in-JS
library |
â |
| useEffectEvent |
useEffectEvent(fn) |
A stable, non-reactive function |
Never; it always sees the latest
props |
Listing it in a dependency array |
2.4.7 |
| Table B.2: Effect
hooks |
Concurrency hooks let React keep an old screen interactive while a
new one renders. use is the odd one out: it may be called inside a
condition or a loop, but never inside try.
| useMemo |
useMemo(calc, deps) |
Cached value |
A dep changes identity |
Memoizing arithmetic that costs less
than the check |
2.7.3 |
| useCallback |
useCallback(fn, deps) |
Cached function |
A dep changes identity |
Using it on a child that is not wrapped
in memo |
2.7.4 |
| useTransition |
useTransition() |
[isPending, startTransition] |
Marks its updates low-priority |
Wrapping an input's own value
update |
2.8.2 |
| useDeferredValue |
useDeferredValue(value, initial?) |
A lagging copy of value |
Re-renders again once the urgent work
lands |
Deferring a value that is cheap to
render |
2.8.4 |
| use |
use(promise) |
The resolved value |
Suspends until the promise settles |
Creating the promise during render |
2.8.6 |
| use (context) |
use(Ctx), legal inside if |
The context value |
Same conditions as useContext |
Reaching for it inside a try block |
2.8.6 |
| useSyncExternalStore |
useSyncExternalStore(sub, get,
getServer?) |
The current snapshot |
The store calls the subscriber |
Returning a new object from
getSnapshot |
2.8.8 |
| Table B.3:
Performance and concurrent rendering hooks |
Form hooks connect a component to an action â the async function a
<form action={...}> or formAction runs. useFormStatus is imported
from react-dom, not react, and reads the form above it.
| useActionState |
useActionState(action, initial,
url?) |
[state, formAction, isPending] |
The action returns a new state |
Forgetting the action's first argument
is prior state |
2.9.3 |
| useFormStatus |
useFormStatus() |
{ pending, data, method, action } |
The enclosing form starts or ends a
submit |
Calling it in the component that renders
<form> |
2.9.4 |
| useOptimistic |
useOptimistic(value, reducer?) |
[optimistic, addOptimistic] |
Reverts when the action settles |
Calling addOptimistic outside a
transition |
2.9.5 |
| useId |
useId() |
A stable string, unique per
instance |
Never changes for a given component |
Using it as a list key |
2.8.7 |
| useDebugValue |
useDebugValue(value, format?) |
Nothing |
Read by React DevTools only |
Calling it in a component instead of a
custom hook |
2.10.10 |
| Table B.4: Form,
identity and debugging hooks |
The React Compiler, stable since version 1.0, inserts memoization at
build time. Where it runs, hand-written memoization is dead weight;
where it cannot see, you still write it yourself.
| useMemo for a derived value |
Caches the expression and its inputs
automatically |
The value is used by non-React code, or
caching is semantic |
2.7.7 |
| useCallback for a stable prop |
Gives every closure a stable
identity |
The identity is compared by
addEventListener or a library |
2.7.7 |
| memo(Component) |
Skips re-rendering unchanged subtrees on
its own |
The child ships pre-compiled from
node_modules |
2.7.2 |
| useRef to dodge a re-render |
Nothing; refs are not memoization |
Always â the compiler does not replace
refs |
2.6.1 |
| Dependency arrays |
Nothing; effect deps stay your
responsibility |
Always â only memoization is
automated |
2.4.2 |
| Opting out |
Honors a "use no memo" directive in a
file |
A component breaks under
auto-memoization |
2.7.8 |
| Table B.5: Hooks the
React Compiler makes unnecessary |
| Call hooks at the top level |
State attaches to the wrong slot |
"Rendered fewer hooks than
expected" |
2.10.2 |
| Never call a hook in a condition or
loop |
The order changes between renders |
State from one field appears in
another |
2.10.2 |
| Call hooks only from components or
hooks |
React has no render to attach to |
"Invalid hook call" from a plain
function |
2.10.2 |
| Name custom hooks useSomething |
The linter stops checking the file |
Missing dependencies go unreported |
2.10.1 |
| Keep render pure |
Concurrent rendering may run it
twice |
Doubled writes under Strict Mode |
2.11.8 |
| List every reactive value in deps |
Effects read stale props and state |
A fetch keeps using yesterday's id |
2.4.2 |
| Table B.6: The rules
of hooks and what each one prevents |
| usePrevious(value) |
The value from the previous render |
useRef plus useEffect |
2.10.3 |
| useFetch(url) |
{ data, error, loading } |
useEffect with an AbortController |
2.10.4 |
| useWindowSize() |
{ width, height } |
A resize listener in useEffect |
2.10.5 |
| useOutsideAlerter(ref, fn) |
Nothing; calls fn on outside clicks |
A document listener plus a ref |
2.10.6 |
| useScript(src) |
'loading', 'ready' or 'error' |
An injected <script> in
useEffect |
2.10.7 |
| useDebounce(value, ms) |
The value, settled after ms |
useState plus a cleared timer |
2.10.8 |
| useLocalStorage(key, initial) |
[value, setValue], persisted |
useState with lazy initialization |
2.10.9 |
| Table B.7: The
custom hooks built in this book |