Performance and Concurrent Features
|
This section documents React 19.x for the web, using function components and Hooks throughout (class components appear only where React still requires them, such as error boundaries). React Native is out of scope and gets only a pointer. The React 19 additions follow the current official React documentation. This content was generated with the assistance of AI and should be verified against react.dev before being relied on in production, since React APIs continue to evolve between releases. This section’s bibliography lists the reference material consulted while preparing these pages. |
React re-renders a component when its state changes, when its parent re-renders, or when a context it consumes changes. Re-rendering is usually cheap. Optimize only after measuring a real problem.
The re-render model and measuring it
When a component re-renders, React also re-renders all of its descendants by default, then diffs the result
and commits only real DOM changes. Slowness comes from doing expensive work in render or re-rendering large
subtrees needlessly.
Measure with the React DevTools Profiler (record an interaction, read the flamegraph) and, in code, the
<Profiler> component:
import { Profiler } from 'react';
<Profiler id="Sidebar" onRender={(id, phase, actualDuration) => log(id, phase, actualDuration)}>
<Sidebar />
</Profiler>
See <Profiler>.
memo, useMemo, useCallback
These skip work when inputs are referentially unchanged (Object.is).
-
memo(Component)— skips re-rendering the component if its props are the same as last render. -
useMemo(fn, deps)— caches the return value offnbetween renders whiledepsare unchanged. -
useCallback(fn, deps)— caches the function itself, so a memoized child does not re-render because it received a "new" callback.
const Row = memo(function Row({ item, onPick }) { /* ... */ });
function List({ items, onPick }) {
const sorted = useMemo(() => [...items].sort(byName), [items]); // expensive calc cached
const handlePick = useCallback((id) => onPick(id), [onPick]); // stable identity
return sorted.map((it) => <Row key={it.id} item={it} onPick={handlePick} />);
}
They do not help — and add overhead — when the component is cheap, when props change every render anyway,
or when the memoized value is trivial to recompute. References:
memo, useMemo,
useCallback.
The React Compiler
The React Compiler is a build-time tool that automatically memoizes components and values, so hand-written
memo / useMemo / useCallback become largely unnecessary. Enable it as a Babel/SWC plugin; it relies on
your components following the Rules of React. See
React Compiler.
Concurrent features
-
useTransition— mark a state update as non-urgent so React can keep the UI responsive and interrupt the slow render if the user types again:const [isPending, startTransition] = useTransition(); function onChange(e) { setQuery(e.target.value); // urgent: the input startTransition(() => setResults(search(e.target.value))); // non-urgent: the list } -
useDeferredValue— render a non-urgent part of the UI with a "lagging" copy of a value while a fast update goes through:const deferredQuery = useDeferredValue(query); const list = useMemo(() => filter(items, deferredQuery), [items, deferredQuery]); -
useSyncExternalStore— subscribe safely to an external store (see Custom Hooks). -
List virtualization — for very long lists, render only the visible rows with a library such as
react-window.
References: useTransition,
useDeferredValue.