liveBook · Manning

useCallback

As explained in React in Depth

useCallback is a React hook that memoizes a function reference between renders according to a dependency array.

useCallback keeps a function reference stable between renders until its dependencies change. Its main use is passing callbacks to memoized components: an inline function is a new value on every render, even when its definition looks identical, so it can defeat a memo() comparison. section 3.2.3 section 3.2.4

Listing 3.10 A to-do list with deletable items
import { memo, useCallback, useState } from "react";
const Items = memo(function Items({ items, onDelete }) {
  return (
    <>
      <h2>Todo items</h2>
      <ul>
        {items.map((todo) => (
          <li key={todo}>
            {todo}
            <button onClick={() => onDelete(todo)}>X</button>
          </li>
        ))}
      </ul>
    </>
  );
});
function Todo() {
  const [items, setItems] = useState(["Clean gutter", "Do dishes"]);
  const [newItem, setNewItem] = useState("");
  const onSubmit = (evt) => {
    setItems((items) => items.concat([newItem]));
    setNewItem("");
    evt.preventDefault();
  };
  const onChange = (evt) => setNewItem(evt.target.value);
  const onDelete = useCallback(    #1
    (item) => setItems((list) => list.filter((i) => i !== item)),
    []    #2
  );
  return (
    <main>
      <Items items={items} onDelete={onDelete} />
      <form onSubmit={onSubmit}>
        <input value={newItem} onChange={onChange} />
        <button>Add</button>
      </form>
    </main>
  );
}
function App() {
  return <Todo />;
}
export default App;

In the deletable to-do list, Items is wrapped in memo(). The parent gives it an onDelete callback created with useCallback; the callback uses a functional state update, so its dependency array can remain empty. Typing into the separate input no longer gives Items a newly created callback prop. section 3.2.3

Choose dependencies for the values the callback uses. Conceptually, useCallback(fn, deps) is useMemo(() => fn, deps): it memoizes the function rather than a calculated result. A stable callback helps only when its identity matters, such as when it crosses a memoized component boundary. Memoization itself has overhead, so avoid adding it to code that already runs well. section 3.2.4 section 3.2.3

Listing 6.6 The playlist with a memoized callback
import { useReorderable } from "./useReorderable";
import "./playlist.css";
import { ActionType, Song } from "./types";
import { PlaylistItem } from "./PlaylistItem";
import { MouseEvent, useCallback } from "react";
interface PlaylistProps {
  songs: Song[];
}
export function Playlist({ songs }: PlaylistProps) {
  const [list, dispatch] = useReorderable(songs);
  const handleMove = useCallback(
    (index: number) =>     #1
     (evt: MouseEvent<HTMLButtonElement>) => {   #1
      const type = evt.currentTarget.name as ActionType;
      dispatch({ type, index });
    },
    [dispatch]
  );
  return (
    <ol>
      {list.map((song, index) => (
        <PlaylistItem
          key={song.id}
          song={song}
          index={index}
          move={handleMove}
        />
      ))}
    </ol>
  );
}

The TypeScript playlist defines handleMove once above the rendered list and passes it to each memoized PlaylistItem. The callback depends on dispatch and explicitly types its index and button event. section 6.1.6

Do not put useCallback inside a list-rendering loop: hooks cannot be called there. Moving the callback above the return also moves it away from the prop that previously supplied TypeScript’s contextual argument types. Type the arguments directly, supply a function type to useCallback, or type the receiving variable. section 6.1.6

How React in Depth covers it

How deep it goes
Works through dependency selection, a functional state update, and typed callback placement; stops short of React hook internals.
Examples
Uses a deletable to-do list and a TypeScript playlist with memoized items and a shared move callback.
What it assumes
Assumes React components, hooks, JavaScript function identity, and, for the playlist, TypeScript callback types.
What you can do after
Apply it via React to keep callback props stable, choose dependencies, and type callbacks outside rendering loops.
How it connects
Connects memo(), useMemo, dependency arrays, referential equality, functional state updates, and the rules of hooks.

Also explained in

Compare how 2 books explain useCallback →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of React in Depth. The section links point to the parts of the book it draws on.