What are useCallback and useMemo in React?
useCallback memoizes a function reference; useMemo memoizes a computed value. Both skip re-computation when their dependencies have not changed.
React
Overview
`useCallback` and `useMemo` are performance optimisation hooks. They memoize (cache) a value between renders, recomputing only when the listed dependencies change. They are tools to prevent unnecessary work - but only when that work is actually expensive.
Key Points
- •`useCallback(fn, deps)` returns the same function reference between renders (unless deps change).
- •`useMemo(fn, deps)` returns the memoized return value of `fn` (not the function itself).
- •Primary use for `useCallback`: pass a stable function to a `React.memo` child so it does not re-render.
- •Primary use for `useMemo`: skip expensive computations (sorting, filtering large lists) on every render.
- •Both have overhead themselves - do not add them "just in case". Profile first.
- •An empty deps array `[]` memoizes forever (never recomputes) - use carefully.
Examples
useCallback: stable function reference for a child component
tsximport { useState, useCallback } from 'react';
const ExpensiveChild = React.memo(({ onSubmit }: { onSubmit: () => void }) => {
console.log('ExpensiveChild rendered');
return <button onClick={onSubmit}>Submit</button>;
});
function Parent() {
const [count, setCount] = useState(0);
// Without useCallback: new function on every render → ExpensiveChild always re-renders
// With useCallback: same reference until dependencies change
const handleSubmit = useCallback(() => {
console.log('submitted');
}, []); // no deps - function never changes
return (
<>
<button onClick={() => setCount(c => c + 1)}>Count: {count}</button>
<ExpensiveChild onSubmit={handleSubmit} />
</>
);
}useMemo: expensive computation
tsximport { useState, useMemo } from 'react';
function ProductList({ products, searchQuery }) {
// Recomputes only when products or searchQuery changes
const filtered = useMemo(() =>
products
.filter(p => p.name.toLowerCase().includes(searchQuery.toLowerCase()))
.sort((a, b) => a.price - b.price),
[products, searchQuery]
);
return <ul>{filtered.map(p => <li key={p.id}>{p.name}</li>)}</ul>;
}Without useMemo, the filter+sort runs on every render - even when only unrelated state changed.
When NOT to use them
tsx// UNNECESSARY: simple values don't need memoization
const double = useMemo(() => count * 2, [count]); // just use: count * 2
// UNNECESSARY: component doesn't use React.memo
const handler = useCallback(() => doThing(), []); // only matters if child is memo'd
// Memoization has a cost (deps comparison) - don't add it without a measurable benefitPro Tip
Add `useCallback` and `useMemo` when you have a measured performance problem - not preemptively. The React DevTools Profiler shows which components render unnecessarily. A good heuristic: `useMemo` for computations that take more than ~1ms; `useCallback` for functions passed to `React.memo`-wrapped components.