Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

useMemo and useCallback

On every render React runs all the component's code again: calculations, function creation. Usually that is fast and unnoticeable. The useMemo and useCallback hooks let you remember (memoize) a value or a function and recreate it only when its dependencies change. This is an optimization: without it the code works the same, just sometimes slower.

useMemo: remembering a calculation

Let us add a post search to the blog. The filtering runs on every render — even when what changed is not the search text but, say, the theme. In src/components/PostSearch.jsx:

// src/components/PostSearch.jsx
import { useMemo, useState } from 'react';
 
export default function PostSearch({ posts }) {
  const [query, setQuery] = useState('');
 
  const found = useMemo(
    () => posts.filter((post) => post.title.includes(query.toLowerCase())),
    [posts, query],
  );
 
  return (
    <>
      <input value={query} onChange={(event) => setQuery(event.target.value)} />
      <p>Found: {found.length}</p>
    </>
  );
}

The first argument is a function that computes the value, the second is the dependencies, as in useEffect. While posts and query stay the same, React returns the saved array without calling filter. Type "qui" — the counter shows how many titles contain those letters.

useCallback: remembering a function

A function declared in a component is created anew on every render. useCallback returns the same function until its dependencies change:

// src/App.jsx
import { useCallback, useState } from 'react';
 
export default function App() {
  const [todos, setTodos] = useState([]);
 
  const handleDelete = useCallback((id) => {
    setTodos((current) => current.filter((todo) => todo.id !== id));
  }, []);
 
  return <TodoList todos={todos} onDelete={handleDelete} />;
}

The setter is called with a function, so todos is not needed in the dependencies and handleDelete is created once. This matters only if TodoList is wrapped in memo (see the performance lesson) or the function is a dependency of an effect.

When it is needed

SituationMemoize?
A filter or sum over a hundred itemsNo
A heavy calculation: thousands of items, complex sortinguseMemo
A function passed to a component wrapped in memouseCallback
A function or object in useEffect dependenciesuseMemo or useCallback
An ordinary button handlerNo

Write code without them first. If the page is slow, find the slow spot with the Profiler in React Developer Tools, and only then add memoization.

React Compiler

There is React Compiler — a build tool that adds memoization to components by itself. In projects where it is enabled, useMemo and useCallback are rarely written by hand. The standard Vite template does not enable it, so knowing these hooks is still useful — they are common in other people's code.

Common mistake

A forgotten dependency makes useMemo return a stale value:

// Wrong: query is not a dependency, so the search ignores typing.
const found = useMemo(
  () => posts.filter((post) => post.title.includes(query)),
  [posts],
);

List every value used inside the function. The project's linter tells you when one is missing.

Thanks for reading!

Donate to the site — we work hard for you.