Chapter 3 of 4
useMemo and useCallback
What each one caches, when it pays off, and what it costs.
useMemo caches a value. useCallback caches a function. Both recompute when their dependencies change and both exist to keep something stable between renders.
const sorted = useMemo(
() => [...products].sort((a, b) => a.price - b.price),
[products]
);
const handleSubmit = useCallback((values) => save(id, values), [id]);
// useCallback(fn, deps) is exactly useMemo(() => fn, deps)Two reasons to use them
- The calculation is genuinely expensive - sorting thousands of rows, parsing a large payload.
- The result is passed to a memoised child or used as an effect dependency, so its identity matters.
If neither applies, you are adding a dependency array to maintain and a cache to hold for no benefit.
// Pointless: the addition is cheaper than the memo bookkeeping
const total = useMemo(() => a + b, [a, b]);
// Worth it: a real computation over a large list
const grouped = useMemo(() => groupByCategory(products), [products]);Common mistakes
// Missing dependency: the callback keeps a stale id forever
const save = useCallback(() => api.save(id), []);
// Object dependency: a new object each render defeats the memo
const options = { limit };
const result = useMemo(() => search(options), [options]);
// Depend on the primitive instead
const result = useMemo(() => search({ limit }), [limit]);The React Compiler
The React Compiler analyses your components and inserts memoisation automatically. Where it is enabled, most manual useMemo and useCallback calls become unnecessary - which is another good reason not to scatter them by hand today.