How Do You Avoid Unnecessary Re-Renders in React?
Cut wasted React re-renders with React.memo, useMemo, useCallback and stable props. Learn to measure, memoize and structure state for smooth performance.
Expected Interview Answer
You avoid unnecessary re-renders by keeping state local, stabilizing props and callbacks, and memoizing components so React skips re-rendering when the relevant inputs have not actually changed.
React re-renders a component when its state or props change, and re-rendering a parent re-renders its children by default. The main tools are React.memo to skip child renders when props are shallow-equal, useMemo to cache expensive derived values, and useCallback to keep function references stable. Just as important is structural work: lifting state only as high as needed, splitting components, and passing stable props so memoization actually holds.
- Faster, more responsive interfaces
- Lower CPU work and battery use on mobile
- Fewer wasted renders of expensive subtrees
- Smoother interactions and better INP
- Predictable performance as the app grows
AI Mentor Explanation
A smart scoreboard operator only updates the panels that actually changed after a delivery — the runs tick over, but the team names and venue stay untouched. Repainting the entire board every ball would waste time and effort. Avoiding unnecessary re-renders is the same discipline: React only redraws the components whose inputs changed rather than the whole scoreboard.
Step-by-Step Explanation
Step 1
Measure first
Use the React Profiler to find components that re-render often or take long, so you optimize real hotspots not guesses.
Step 2
Keep state local
Store state as close to where it is used as possible so a change re-renders the smallest subtree.
Step 3
Memoize components
Wrap pure presentational children in React.memo so they skip re-rendering when props are shallow-equal.
Step 4
Stabilize props
Use useCallback for handlers and useMemo for objects/arrays so referential equality holds across renders.
Step 5
Cache expensive work
Wrap costly computations in useMemo so they only recompute when their dependencies change.
What Interviewer Expects
- Knowing what triggers a re-render (state and prop changes)
- Correct use of React.memo, useMemo and useCallback
- Why stable references matter for memoization to work
- Structural fixes like lifting state and splitting components
- Measuring with the Profiler before optimizing
Common Mistakes
- Wrapping everything in memo without measuring, adding overhead
- Passing new inline objects or arrow functions that break React.memo
- Putting state too high in the tree, re-rendering large subtrees
- Assuming useMemo guarantees caching rather than being a hint
- Optimizing render count instead of actual user-perceived slowness
Best Answer (HR Friendly)
“React redraws parts of the screen when data changes, and sometimes it redraws more than needed. You speed things up by telling React to reuse components whose inputs did not change and by keeping data close to where it is used, so only the parts that truly changed get redrawn.”
Code Example
import { memo, useCallback, useMemo, useState } from 'react'
const ExpensiveList = memo(function ExpensiveList({ items, onSelect }) {
console.log('rendering list')
return (
<ul>
{items.map((item) => (
<li key={item.id} onClick={() => onSelect(item.id)}>
{item.label}
</li>
))}
</ul>
)
})
export default function App() {
const [count, setCount] = useState(0)
const [items] = useState([
{ id: 1, label: 'Alpha' },
{ id: 2, label: 'Beta' },
])
// Stable reference: the list will not re-render when only count changes
const handleSelect = useCallback((id) => {
console.log('selected', id)
}, [])
const total = useMemo(() => items.length, [items])
return (
<div>
<button onClick={() => setCount((c) => c + 1)}>Count: {count}</button>
<p>Total items: {total}</p>
<ExpensiveList items={items} onSelect={handleSelect} />
</div>
)
}Follow-up Questions
- What is the difference between useMemo and useCallback?
- Why can React.memo still re-render even when props look the same?
- How does the React Profiler help you find wasted renders?
- How does lifting state down or colocating it reduce re-renders?
- When is memoization not worth the added complexity?
MCQ Practice
1. What does React.memo do?
React.memo performs a shallow comparison of props and skips re-rendering the component when they are unchanged.
2. Why might passing an inline arrow function break React.memo?
A new function identity every render fails the shallow prop equality check, so the memoized child re-renders anyway.
3. Which hook caches an expensive computed value between renders?
useMemo recomputes a value only when its dependency array changes, otherwise it returns the cached result.
Flash Cards
What triggers a React re-render? — A change to the component's state or props; a parent render re-renders children by default.
What does React.memo compare? — It does a shallow equality check of props and skips re-rendering when they are unchanged.
Why use useCallback? — To keep a function's reference stable across renders so memoized children do not re-render needlessly.
First step before optimizing renders? — Measure with the React Profiler to find real hotspots instead of guessing.