React Advanced: Performance, Patterns and Production
Rendering and Performance
In this lesson you will
- Explain what triggers a re-render and what React does
- Use memoisation and state placement to avoid wasted work
- Measure performance with the Profiler before optimising
Introduction
React is fast by default for most apps. Performance problems appear with big lists, heavy calculations, or components that re-render far more often than needed. The key is to understand the process, measure where time goes, and fix only what matters.
What triggers a render
A component re-renders when:
- Its own state changes
- Its parent re-renders (and it is not memoised)
- A context value it uses changes
Rendering means React calls your component function to work out what the UI should look like. It then compares (reconciles) the result with the previous one, and updates only the parts of the real page that changed. Rendering is usually cheap. What is costly is slow code inside the component, or rendering thousands of elements.
Keep state close, split components
State at the top re-renders everything below it. If only one small part needs fast-changing state, move that part into its own component:
React (JSX)
// Typing in the input re-renders the whole page, including HeavyChart
function Page() {
const [text, setText] = useState("");
return (
<>
<input value={text} onChange={(e) => setText(e.target.value)} />
<HeavyChart />
</>
);
}
// Better: the state lives in a small component of its own
function Page() {
return (
<>
<SearchInput />
<HeavyChart />
</>
);
}In the second version, typing re-renders only SearchInput. This often removes the need for any memoisation.
Quick quiz
Which fix often removes unnecessary re-renders without any special API?
memo, useMemo and useCallback
React.memoskips re-rendering a component if its props are unchanged:
React (JSX)
import { memo } from "react";
const Row = memo(function Row({ item, onSelect }) {
return <li onClick={() => onSelect(item.id)}>{item.name}</li>;
});useMemocaches the result of an expensive calculation.useCallbackcaches a function, so a memoised child receives the same function each time.
These only help if the props really stay the same. A new object or function created during each render defeats memo, which is why useCallback and useMemo are used together with it. Do not add them everywhere. They have a cost, and the newer React Compiler can add much of this automatically. Measure first.
Keys
Stable, unique key values let React reuse the right elements when a list changes. Using array positions as keys for lists that reorder causes wasted work and bugs, such as input values jumping between rows.
Long lists
Rendering thousands of rows is slow. Use virtualisation (also called windowing), which renders only the rows currently visible, with libraries such as react-window or TanStack Virtual. Pagination and search can also reduce the amount.
Heavy updates: transitions
For updates that are slow but not urgent, such as filtering a big list while typing, mark them as a transition so React keeps the input responsive:
React (JSX)
import { useState, useTransition } from "react";
const [isPending, startTransition] = useTransition();
// startTransition(() => setFilter(text));Loading less code
- Code splitting with
React.lazyandSuspenseloads rarely used parts only when needed. - Keep bundle size small: check dependencies, and avoid importing whole libraries for one function.
- Optimise images and fonts, which often matter more than JavaScript.
Measure first
Use the React Developer Tools Profiler to record interactions and see which components render, how often and for how long. Use the browser's performance tools for the bigger picture. The process:
- Find a real slowness that users would notice.
- Measure it.
- Make one change.
- Measure again.
Try it yourself
Take a page with a text input and a slow component. Record with the Profiler, move the state into its own component, record again and compare.
Summary
- Components re-render when their state, their parent or their context changes, and rendering is usually cheap.
- Keep state local, use memo, useMemo and useCallback sparingly, and virtualise long lists.
- Measure with the Profiler before and after every optimisation.
Lesson complete
+20 XP
Nice work. Keep going while it is fresh.
Sources and further reading