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

tsx
import { 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

tsx
import { 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 benefit

Pro 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.