Part 19: useCallback Hook: Function Memoization & Optimization

Part 19 Technology Updated September 12, 2026

Part 19: useCallback Hook: Function Memoization & Optimization

While useMemo caches the result of a calculation, useCallback caches a function definition between renders to maintain referential equality.


1. The Referential Equality Problem

In JavaScript, functions are objects. Every time a component re-renders, any function declared inside it is recreated with a new memory address:

// Render 1:
const fn1 = () => {};

// Render 2:
const fn2 = () => {};

fn1 === fn2; // false! Different memory references!

If you pass this function as a prop to an optimized child component wrapped in React.memo, the child will re-render anyway because it sees a "new" prop!


2. Solving It with useCallback

import { useState, useCallback, memo } from 'react';

// Memoized child component: only re-renders if props change
const DeleteButton = memo(function DeleteButton({ onDelete, id }) {
  console.log(`Rendering DeleteButton for item ${id}`);
  return <button onClick={() => onDelete(id)}>Delete</button>;
});

export default function ItemManager() {
  const [items, setItems] = useState(['Item A', 'Item B', 'Item C']);
  const [counter, setCounter] = useState(0);

  // handleDelete maintains the same function reference across renders
  const handleDelete = useCallback((id) => {
    setItems(prev => prev.filter((_, idx) => idx !== id));
  }, []);

  return (
    <div>
      <button onClick={() => setCounter(c => c + 1)}>
        Rerender Parent (Counter: {counter})
      </button>

      <ul>
        {items.map((item, idx) => (
          <li key={idx}>
            {item} <DeleteButton onDelete={handleDelete} id={idx} />
          </li>
        ))}
      </ul>
    </div>
  );
}

Key Takeaways

  • useCallback memoizes the function instance itself.
  • It is primarily useful when passing callbacks to child components optimized with React.memo.
  • Always declare all variables used inside the callback in its dependency array.

In Part 20, we will eliminate prop drilling using the useContext Hook!