useMemo & useCallback

What memoization is, caching a calculation's result and an object reference with useMemo, caching a function reference with useCallback, and when NOT to use them -- with simple examples.

Intermediate 12 min
TR

useMemo & useCallback

So far we've seen useState, useEffect, and useRef. This lesson covers two performance hooks -- useMemo and useCallback. These two are a bit different from the others: knowing how to use them matters, but knowing WHEN NOT to use them matters just as much.

What Is Memoization?

Memoization is a technique for storing the result of a calculation so that, if it's requested again with the same inputs, you get that result back WITHOUT repeating the calculation. React components often re-render frequently; redoing the same expensive calculation or recreating the same function on every render can sometimes be an unnecessary cost -- useMemo and useCallback exist to avoid that cost.

Caching a Calculation's Result with useMemo

useMemo caches the RESULT of a calculation:

import { useState, useMemo } from "react";

function slowSquare(n) {
  // Kasıtlı olarak YAVAŞ bir hesaplama -- gerçek bir "pahalı hesaplama"yı
  // simüle etmek için.
  let result = 0;
  for (let i = 0; i < 100000000; i++) {
    result = n * n;
  }
  return result;
}

function UseMemoBasicExample() {
  const [number, setNumber] = useState(5);
  const [theme, setTheme] = useState("light");

  // useMemo, `number` değişmediği sürece slowSquare'i TEKRAR ÇALIŞTIRMAZ --
  // önceki sonucu hafızada tutar (memoize eder) ve onu döndürür.
  const squared = useMemo(() => slowSquare(number), [number]);

  return (
    <div>
      <p>
        {number} karesi: {squared}
      </p>
      <button onClick={() => setNumber(number + 1)}>Sayıyı Artır</button>
      <button onClick={() => setTheme(theme === "light" ? "dark" : "light")}>
        Temayı Değiştir ({theme})
      </button>
    </div>
  );
}

useMemo(() => slowSquare(number), [number]) does NOT re-run slowSquare as long as number hasn't changed -- it returns the previous result. Changing the theme re-renders the component, but since number stays the same, the slow calculation doesn't run again.

Keeping an Object Reference Stable with useMemo

useMemo isn't just for "expensive" calculations -- it's also used to keep an object's/array's REFERENCE (identity) stable across renders:

import { useState, useMemo } from "react";

function ObjectMemoizationExample() {
  const [count, setCount] = useState(0);

  // useMemo olmadan, her render'da YENİ bir nesne oluşur -- içeriği aynı
  // olsa bile, iki farklı render'daki nesneler "aynı" (===) SAYILMAZ.
  const config = useMemo(
    () => ({ theme: "dark", fontSize: 16 }),
    [], // Bağımlılık yok -- nesne yalnızca İLK render'da oluşturulur.
  );

  return (
    <div>
      <p>Sayaç: {count}</p>
      <p>
        Tema: {config.theme}, Font Boyutu: {config.fontSize}
      </p>
      <button onClick={() => setCount(count + 1)}>+1</button>
    </div>
  );
}

Without useMemo, { theme: "dark", fontSize: 16 } would be a NEW object on every render -- even with identical contents, two separate objects aren't considered equal when compared with === in JavaScript. useMemo returns the SAME object as long as the dependency array hasn't changed.

Keeping a Function Reference Stable with useCallback

useCallback is very similar to useMemo, but it memoizes a FUNCTION instead of a value:

import { useState, useCallback } from "react";

function UseCallbackBasicExample() {
  const [count, setCount] = useState(0);

  // useCallback olmadan, her render'da handleClick YENİ bir fonksiyon olarak
  // oluşturulur -- iki farklı render'daki fonksiyonlar "aynı" sayılmaz.
  // useCallback, dependency array değişmediği sürece AYNI fonksiyon
  // referansını korur.
  const handleClick = useCallback(() => {
    console.log("Tıklandı, count:", count);
  }, [count]);

  return (
    <div>
      <p>Sayaç: {count}</p>
      <button onClick={() => setCount(count + 1)}>+1</button>
      <button onClick={handleClick}>Console'a Yaz</button>
    </div>
  );
}

Without useCallback, handleClick would be a NEW function on every render. useCallback keeps the SAME function reference as long as the dependency array ([count]) hasn't changed.

When NOT to Use Them

useMemo and useCallback have their own cost -- comparing the dependency array and holding onto the result. For simple, fast operations, that cost can be more expensive than what it saves:

import { useState, useMemo } from "react";

function WhenNotToUseMemoExample() {
  const [count, setCount] = useState(0);

  // GEREKSİZ: Bu toplama işlemi zaten çok hızlı -- useMemo'nun kendi
  // maliyeti (karşılaştırma yapmak, sonucu hafızada tutmak), kazandırdığından
  // daha pahalı olabilir.
  // const doubled = useMemo(() => count * 2, [count]);

  // DAHA İYİ: Basit, hızlı hesaplamalar için useMemo'ya gerek yok.
  const doubled = count * 2;

  return (
    <div>
      <p>
        {count} x 2 = {doubled}
      </p>
      <button onClick={() => setCount(count + 1)}>+1</button>
    </div>
  );
}

Wrapping something as simple as count * 2 in useMemo has no real benefit -- the operation is already nearly instant. Use useMemo/ useCallback only when there's a genuinely expensive calculation, or when a value's/function's reference needs to stay stable (for example, because it's used in another hook's dependency array). Otherwise you end up with code that's more complex, but not actually faster -- this is called "premature optimization."

Summary and Glossary

useMemo caches the result of a calculation or an object's reference; useCallback does the same thing for a function. Both return the previous result/reference as long as the dependency array hasn't changed. For simple, fast operations you don't need these hooks -- their own cost can be bigger than the problem they solve.

Glossary

Memoization — A technique for caching a calculation's result and returning it, without repeating the calculation, when requested again with the same inputs.

useMemo — The hook that memoizes a calculation's result or an object/array reference.

useCallback — The hook that memoizes a function reference.

Premature Optimization — Making an optimization that isn't really needed, doesn't provide a real performance gain, but adds complexity to the code.