1. Introduction đ
useMemo is a performance-focused Hook that lets you cache the result of an expensive calculation between renders. This tutorial covers when and how to use useMemo effectively, how it differs from related Hooks like useCallback and React.memo, and when it's better left out entirely.
Information
2. What is useMemo? đ¤
useMemo is a Hook that memoizes the result of a calculation, recomputing it only when its dependencies change â instead of on every single render.
Code Snippet
import { useMemo } from 'react';
function ProductList({ products, searchTerm }) {
const filteredProducts = useMemo(
() => products.filter((p) => p.name.includes(searchTerm)),
[products, searchTerm]
);
return <ul>{filteredProducts.map((p) => <li key={p.id}>{p.name}</li>)}</ul>;
}3. Why Use useMemo? đĄ
- Avoid Redundant Work: Skip expensive calculations when their inputs haven't changed.
- Stable References: Prevent unnecessary re-renders in memoized child components that receive computed objects or arrays.
- Smoother UI: Reduce jank in components with computationally heavy rendering logic.
Caution
4. Memoization đ§
Memoization is a general programming technique where a function's result is cached based on its inputs, so repeated calls with the same inputs return the cached result instead of recomputing.
Code Snippet
// Without memoization: recalculates every single render
const total = calculateTotal(items);
// With memoization: recalculates only when 'items' changes
const total = useMemo(() => calculateTotal(items), [items]);5. How useMemo Works âī¸
On the first render, React runs the function and stores its result along with the current dependency values. On subsequent renders, React compares the new dependencies against the stored ones â if they match, it returns the cached result without re-running the function.
6. Syntax đ
Code Snippet
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);- The first argument is a function that returns the value to memoize.
- The second argument is the dependency array controlling when to recompute.
Important
7. Dependency Array đ
React recomputes the memoized value only when at least one value in the dependency array has changed since the last render, compared using Object.is().
Code Snippet
const sortedItems = useMemo(
() => [...items].sort((a, b) => a.price - b.price),
[items] // recomputes only when 'items' changes
);Note
8. Memoizing Expensive Calculations đ§Ž
Code Snippet
function Dashboard({ transactions }) {
const summary = useMemo(() => {
console.log("Recalculating summary..."); // only logs when 'transactions' changes
return transactions.reduce(
(acc, t) => ({
total: acc.total + t.amount,
count: acc.count + 1,
}),
{ total: 0, count: 0 }
);
}, [transactions]);
return <p>Total: ${summary.total} across {summary.count} transactions</p>;
}Tip
9. Memoizing Derived Values đ
Code Snippet
function OrderSummary({ items, taxRate }) {
const subtotal = useMemo(
() => items.reduce((sum, item) => sum + item.price * item.quantity, 0),
[items]
);
const total = useMemo(() => subtotal * (1 + taxRate), [subtotal, taxRate]);
return <p>Total: ${total.toFixed(2)}</p>;
}Note
10. Memoizing Objects đ§ą
Memoizing an object prevents a new reference from being created on every render, which is especially useful when passing that object as a prop to a memoized child component.
Code Snippet
function Chart({ data, color }) {
const chartConfig = useMemo(
() => ({ data, color, animation: true, responsive: true }),
[data, color]
);
return <ThirdPartyChart config={chartConfig} />;
}11. Memoizing Arrays đ
Code Snippet
function ProductGrid({ products, category }) {
const filteredProducts = useMemo(
() => products.filter((p) => p.category === category),
[products, category]
);
return (
<div className="grid">
{filteredProducts.map((p) => <ProductCard key={p.id} product={p} />)}
</div>
);
}Tip
12. Preventing Unnecessary Computations đĢ
Without useMemo â Recalculates on Every Render
function SearchResults({ items, query, unrelatedState }) {
// Recalculates every time ANY state changes, even unrelated state
const results = items.filter((item) => item.name.includes(query));
// ...
}With useMemo â Only Recalculates When Needed
function SearchResults({ items, query, unrelatedState }) {
const results = useMemo(
() => items.filter((item) => item.name.includes(query)),
[items, query] // unaffected by 'unrelatedState' changes
);
}13. useMemo vs useCallback âī¸
| Aspect | useMemo | useCallback |
|---|---|---|
| Memoizes | The result of a function call | The function itself |
| Typical Use | Expensive computed values | Stable callback references for child props |
Code Snippet
// useMemo: caches the computed VALUE
const total = useMemo(() => calculateTotal(items), [items]);
// useCallback: caches the FUNCTION itself
const handleClick = useCallback(() => addItem(id), [id]);
// useCallback(fn, deps) is equivalent to useMemo(() => fn, deps)14. useMemo vs React.memo âī¸
| Aspect | useMemo | React.memo |
|---|---|---|
| Applies To | A value computed inside a component | An entire component |
| Prevents | Recalculating a value unnecessarily | Re-rendering when props are unchanged |
Note
15. useMemo vs useRef âī¸
| Aspect | useMemo | useRef |
|---|---|---|
| Purpose | Cache a computed value based on dependencies | Persist a mutable value with no automatic recomputation |
| Recalculation | Automatic, based on dependency array | Manual â you control when .current changes |
Caution
16. Performance Optimization âĄ
useMemo trades extra memory usage for avoided computation. It's most valuable when a calculation is genuinely expensive relative to the cost of the comparison check itself.
- Large array transformations (sorting, filtering, mapping over thousands of items).
- Complex mathematical or statistical computations.
- Stabilizing object/array references passed to memoized children.
Warning
17. Dependency Management đ
Code Snippet
function Component({ items, threshold }) {
const filtered = useMemo(
() => items.filter((item) => item.value > threshold),
[items, threshold] // include every value read inside the function
);
}Important
18. Stale Values đ°ī¸
â Stale Value â Missing Dependency
function Cart({ items, discountRate }) {
const total = useMemo(
() => items.reduce((sum, i) => sum + i.price, 0) * (1 - discountRate),
[items] // â missing 'discountRate' â total won't update when discount changes
);
}â Correct Dependencies
function Cart({ items, discountRate }) {
const total = useMemo(
() => items.reduce((sum, i) => sum + i.price, 0) * (1 - discountRate),
[items, discountRate] // â
all inputs included
);
}Tip
19. Common Use Cases đ¨
- Filtering and sorting large lists based on user input.
- Aggregating data for charts, dashboards, or summaries.
- Stabilizing props passed into memoized child components.
- Expensive formatting, like generating complex derived strings or structures.
20. When Not to Use useMemo đĢ
- For cheap calculations (simple arithmetic, string concatenation, small array operations).
- When the value doesn't get passed to a memo-wrapped component or used in another Hook's dependency array.
- As a default habit applied to every computed value "just in case."
Best Practice
21. TypeScript with useMemo đˇ
Code Snippet
interface Product {
id: string;
name: string;
price: number;
}
function ProductList({ products, query }: { products: Product[]; query: string }) {
const filtered: Product[] = useMemo(
() => products.filter((p) => p.name.includes(query)),
[products, query]
);
return <ul>{filtered.map((p) => <li key={p.id}>{p.name}</li>)}</ul>;
}Tip
22. Best Practices đ
- Reserve useMemo for genuinely expensive calculations, not trivial ones.
- Always include every value used inside the function in the dependency array.
- Combine with React.memo when stabilizing props for a memoized child component.
- Profile before optimizing â confirm a real bottleneck exists before reaching for useMemo.
- Keep the memoized function pure, with no side effects.
23. Common Mistakes đĢ
- Wrapping every value in useMemo, even trivial ones, adding unnecessary overhead.
- Omitting dependencies, leading to stale, out-of-sync memoized values.
- Treating useMemo as a guarantee rather than an optimization hint React may discard.
- Performing side effects (like API calls) inside the memoized function.
- Using useMemo when the underlying issue is actually an unnecessary re-render higher up the component tree.
Danger
24. Frequently Asked Questions â
Question
Answer
Question
Answer
Question
Answer
25. Summary đ
useMemo caches the result of a computation between renders, recalculating only when its dependencies change. It's a valuable tool for expensive calculations and stabilizing object or array references â but should be applied deliberately, based on measured performance needs, rather than by default.