useCallback

Memoize a function so its identity stays stable across renders.

Syntaxconst handler = useCallback(() => doThing(id), [id]);

useCallback returns a memoized function that keeps the same identity between renders as long as its dependencies do not change. It is essentially useMemo for functions.

Why function identity matters

Every render creates new function objects. If you pass a callback to a component wrapped in memo, a fresh function each render would defeat the memoization. useCallback keeps the reference stable so the child can skip re-rendering.

Example

Example Β· javascript
import { useCallback, useState } from 'react';
import { memo } from 'react';

const Child = memo(function Child({ onClick }) {
  return <button onClick={onClick}>Click</button>;
});

function Parent() {
  const [count, setCount] = useState(0);
  const handleClick = useCallback(() => {
    setCount((c) => c + 1);
  }, []);
  return (
    <div>
      <p>{count}</p>
      <Child onClick={handleClick} />
    </div>
  );
}

When to use it

  • A memoized list item component receives an onDelete callback; useCallback keeps the callback reference stable so the item does not re-render every time the parent state changes.
  • A custom useDebounce hook wraps the debounced function in useCallback so callers receive a stable reference that can safely be added to dependency arrays.
  • A paginated table passes a fetchPage callback to a child pagination control; useCallback prevents the control from re-rendering whenever unrelated parent state updates.

More examples

Stable callback for memoized child

useCallback gives handleDelete a stable identity so the memo'd ListItem is not re-rendered when filter changes.

Example Β· jsx
import { useState, useCallback, memo } from 'react';

const ListItem = memo(({ item, onDelete }) => (
  <li>{item.name} <button onClick={() => onDelete(item.id)}>X</button></li>
));

function List({ items }) {
  const [filter, setFilter] = useState('');

  const handleDelete = useCallback(id => {
    console.log('delete', id);
  }, []);

  return (
    <>
      <input value={filter} onChange={e => setFilter(e.target.value)} />
      <ul>{items.map(i => <ListItem key={i.id} item={i} onDelete={handleDelete} />)}</ul>
    </>
  );
}

Callback with dependencies

Memoizes a callback that captures userId; the function is recreated only when onSearch or userId changes.

Example Β· jsx
import { useCallback } from 'react';

function SearchForm({ onSearch, userId }) {
  const handleSubmit = useCallback(
    (query) => {
      onSearch({ userId, query });
    },
    [onSearch, userId]
  );

  return <SearchInput onSubmit={handleSubmit} />;
}

useCallback vs useMemo equivalence

Shows that useCallback is syntactic sugar over useMemo returning a function, clarifying when to use each.

Example Β· jsx
import { useCallback, useMemo } from 'react';

// These are equivalent:
const handleClick = useCallback(() => doSomething(x), [x]);
// same as:
const handleClickAlt = useMemo(() => () => doSomething(x), [x]);

// Prefer useCallback for functions β€” it is clearer in intent.

Discussion

  • Be the first to comment on this lesson.