React.memo
Overview
By default, React uses a very simple rule for rendering: If a Parent component re-renders, ALL of its children re-render recursively, regardless of whether the child's specific props actually changed.
In most apps, this is completely fine because React's Virtual DOM diffing is incredibly fast. However, if a child component is extremely heavy (like a Data Grid rendering 5,000 rows, or a complex SVG chart), re-rendering it every time the parent updates a completely unrelated state (like a tiny text input) will cause massive lag.
To prevent this, you can wrap the heavy child component in `React.memo` (short for memoization). React.memo tells React: 'Before you re-render this child, look at its props. If the props are exactly the same as they were during the last render, skip this component entirely and reuse the old HTML.'
Syntax
import { memo, useState } from 'react';
// 1. We wrap the component export in memo()
const ExpensiveChart = memo(function ExpensiveChart({ data }) {
// Imagine this takes 500ms to calculate and render
console.log("Chart is rendering!");
return <div className="heavy-chart">...</div>;
});
function Dashboard() {
const [text, setText] = useState('');
const [chartData, setChartData] = useState([1, 2, 3]);
return (
<div>
{/* 2. Typing in this input updates 'text', causing Dashboard to re-render. */}
{/* WITHOUT memo, ExpensiveChart would re-render on every single keystroke! */}
{/* WITH memo, React sees 'chartData' hasn't changed, and skips the chart render. */}
<input value={text} onChange={(e) => setText(e.target.value)} />
<ExpensiveChart data={chartData} />
</div>
);
}Common Pitfalls
- Overusing React.memo: Do NOT wrap every single component in
memo. Running the prop comparison equation actually costs CPU time. If a component is lightweight (like a simple Button or text block), the prop comparison often takes longer than just letting React re-render the component. Only usememowhen profiling proves a specific component is causing noticeable lag.
Interview Questions
React.memo perform?React.memo performs a shallow comparison (===) of the props. This means if you pass a primitive (string, number), it works perfectly. But if you pass a newly created object or array (data={[1, 2, 3]}), the shallow comparison will always fail (because the memory reference is new), and the component will re-render anyway, completely defeating the purpose of memoization.
React.memo that is breaking due to complex object props?You have two options. You can either use useMemo/useCallback in the parent to ensure the object/function memory reference doesn't change, OR you can pass a custom comparison function as the second argument to React.memo, manually telling React exactly how to compare the old and new props.
Real-World Example
Memoizing a Data Grid Row: Large lists and data grids are the #1 use case for React.memo. When dealing with thousands of nodes, skipping render cycles is critical to maintaining a 60 FPS scroll and interaction rate.
import { memo } from 'react';
// In a table with 10,000 rows, editing Row #5 causes the Parent Table to re-render.
// Without memo, React would re-render all 10,000 rows just to update 1.
// With memo, React instantly skips the 9,999 rows that haven't changed.
const TableRow = memo(({ rowData, onEdit }) => {
return (
<tr>
<td>{rowData.id}</td>
<td>{rowData.name}</td>
<td>{rowData.status}</td>
<td><button onClick={() => onEdit(rowData.id)}>Edit</button></td>
</tr>
);
});
export default TableRow;Check Your Knowledge
Test your understanding of React.memo with these quick questions.