React useMemo
useMemo recomputes and caches the result of an expensive calculation only when its dependencies change, skipping the work on renders where nothing relevant changed.
The Problem: Recomputing on Every Render
A component re-renders whenever its state or props change — including state that has nothing to do with a particular calculation. If that calculation is expensive (sorting a large list, filtering thousands of rows, running heavy math), redoing it on every unrelated render wastes CPU time for no visible benefit.
Caching a Value With useMemo
useMemo(calculateValue, deps) runs calculateValue once, remembers the result, and only re-runs it when one of the values in deps changes between renders. On every other render, it just hands back the cached value from the previous run — no recomputation.
- Filtering or sorting a large array before rendering it
- Deriving a value from props or state that requires a real loop or algorithm, not just a property lookup
- Building a value (like a Set, Map, or computed object) that a memoized child depends on for referential equality
- Avoiding re-running a costly calculation that's unrelated to the state that just changed
Without useMemo: Sorting Runs on Every Render
import { useState } from 'react';
function ProductList({ products, query }) {
console.log('Sorting products...');
const sorted = [...products].sort((a, b) => a.price - b.price);
const visible = sorted.filter((p) =>
p.name.toLowerCase().includes(query.toLowerCase())
);
return (
<ul>
{visible.map((p) => (
<li key={p.id}>{p.name} - ${p.price}</li>
))}
</ul>
);
}
// Every keystroke in an unrelated input elsewhere in the app that
// causes ProductList to re-render also re-sorts and re-filters
// the entire product array from scratch.
const products = [
{ id: 1, name: 'Widget', price: 25 },
{ id: 2, name: 'Gadget', price: 15 },
{ id: 3, name: 'Gizmo', price: 40 },
];
export default function App() {
const [count, setCount] = useState(0);
return (
<div>
<button onClick={() => setCount(count + 1)}>
Re-render parent ({count})
</button>
<ProductList products={products} query="g" />
</div>
);
}
With useMemo: Sorting Only Reruns When Inputs Change
import { useMemo, useState } from 'react';
function ProductList({ products, query }) {
const visible = useMemo(() => {
console.log('Sorting products...');
const sorted = [...products].sort((a, b) => a.price - b.price);
return sorted.filter((p) =>
p.name.toLowerCase().includes(query.toLowerCase())
);
}, [products, query]);
return (
<ul>
{visible.map((p) => (
<li key={p.id}>{p.name} - ${p.price}</li>
))}
</ul>
);
}
const products = [
{ id: 1, name: 'Widget', price: 25 },
{ id: 2, name: 'Gadget', price: 15 },
{ id: 3, name: 'Gizmo', price: 40 },
];
export default function App() {
const [count, setCount] = useState(0);
return (
<div>
<button onClick={() => setCount(count + 1)}>
Re-render parent ({count})
</button>
<ProductList products={products} query="g" />
</div>
);
}
Memoizing a Derived Object for a Memoized Child
import React, { useMemo, useState } from 'react';
const StatsPanel = React.memo(function StatsPanel({ summary }) {
console.log('StatsPanel rendered');
return (
<p>
Total: {summary.total}, Average: {summary.average.toFixed(2)}
</p>
);
});
function Dashboard({ orders }) {
const [theme, setTheme] = useState('light');
// Without useMemo, this object literal is brand-new every render,
// so StatsPanel's React.memo check always sees "changed props".
const summary = useMemo(() => {
const total = orders.reduce((sum, o) => sum + o.amount, 0);
return { total, average: orders.length ? total / orders.length : 0 };
}, [orders]);
return (
<div>
<button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
Toggle theme
</button>
<StatsPanel summary={summary} />
</div>
);
}
const sampleOrders = [
{ id: 1, amount: 50 },
{ id: 2, amount: 75 },
{ id: 3, amount: 120 },
];
export default function App() {
return <Dashboard orders={sampleOrders} />;
}
Exercise: React useMemo
What does useMemo memoize between renders?