useMemo & useCallback — React

Tiap kali parent re-render, komponen child ikut re-render — itu default React. Buat 99% kasus ini gak masalah (React Virtual DOM cepat). Tapi ada 1% kasus perfo

Tiap kali parent re-render, komponen child ikut re-render — itu default React. Buat 99% kasus ini gak masalah (React Virtual DOM cepat). Tapi ada 1% kasus performa kena: child komponen yang RENDER berat (chart, table 1000 row), atau komponen di-wrap React.memo yang butuh referensi prop yang stabil. Solusi: useMemo (cache nilai komputasi) + useCallback (cache function reference). DUA HOOK INI PEDANG BERMATA DUA — pakai salah konteks = bukan performance optimization, tapi memory overhead tanpa benefit. Kebanyakan project gak butuh ini sama sekali.

⚠️ Premature optimization is the root of all evil. Quote ini umur 50 tahun, masih relevan. Jangan asal useMemo/useCallback everywhere — itu memory leak + cognitive overhead. Pakai HANYA setelah profiler nunjukin masalah konkret. React 19 + React Compiler malah otomatis handle ini, jadi useMemo/useCallback manual makin jarang dibutuhin.

useMemo — Cache Nilai Komputasi

function App({ items, query }) {
  // Filter ribuan items — mahal, tapi cuma re-run kalau items/query berubah
  const filtered = useMemo(
    () => items.filter(i => i.name.includes(query)),
    [items, query]
  )

  return <List items={filtered} />
}

Tanpa useMemo, filter jalan tiap render parent — walaupun items/query gak berubah. Dengan useMemo, hasil filter di-cache.

useCallback — Cache Function Reference

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

  // Tanpa useCallback: function baru tiap render → ExpensiveChild re-render
  // Dengan useCallback: function reference SAMA tiap render → ExpensiveChild skip re-render
  const handleClick = useCallback(() => {
    setCount(c => c + 1)
  }, [])

  return <ExpensiveChild onClick={handleClick} />
}

const ExpensiveChild = memo(function ({ onClick }) {
  console.log("expensive render!")
  return <button onClick={onClick}>Klik</button>
})

Note: useCallback cuma berguna kalau child di-wrap memo(). Tanpa memo, useCallback gak ada efek (child tetep re-render karena parent re-render).

Equivalence

// Ini sama persis:
useMemo(() => fn, deps)
useCallback(fn, deps)

// useCallback adalah shortcut untuk useMemo yang return function

Real Use Case

// ✓ BAGUS — komputasi mahal yang dependent
const sortedExpensive = useMemo(
  () => bigList.sort((a, b) => /* complex comparison */),
  [bigList]
)

// ✗ SIA-SIA — komputasi murah, useMemo overhead malah jadi lambat
const doubled = useMemo(() => count * 2, [count])  // langsung aja `count * 2`!

// ✓ BAGUS — pass ke memo() child + butuh stable reference
const handleAction = useCallback((id) => deleteItem(id), [deleteItem])
return <MemoizedTable onDelete={handleAction} />

// ✗ SIA-SIA — child gak di-memo, useCallback gak ngaruh
const handleClick = useCallback(() => alert("hi"), [])
return <button onClick={handleClick}>Klik</button>  // pakai inline arrow aja

🎭 Analogi sehari-hari

useMemo itu kayak catatan di pojok meja warung "rebus telur 5 butir, butuh 8 menit" — kalo customer pesen sama, gak perlu eksperimen ulang, lihat catatan langsung. Tapi kalo pesanannya tiap orang beda (dependency selalu berubah), nyatet jadi sia-sia, malah bikin meja penuh kertas. useCallback kayak kartu nama yang sama tiap kasih ke kenalan — kalo kartu nama identik tiap kali ketemu, kenalan gak perlu update kontak (memo skip re-render). Tapi kalo kartu nama selalu cetak baru, biarpun isinya sama, kenalan kira "ah ini orang beda" (re-render). Dua hook = optimisasi kasus spesifik, jangan asal pasang di mana-mana.

⚠️ Jebakan yang sering ditemui

🎯 Pakai useMemo/useCallback atau enggak?

  • Default: GAK pake. React cukup cepat di kebanyakan case.
  • Komputasi >100ms (filter/sort 10k+ items, kalkulasi kompleks) → useMemo
  • Pass function ke memo() child + child render mahal → useCallback
  • Object/array as prop ke memo() child → useMemo di parent untuk stable reference
  • deps di useEffect yang tergantung object/function → wrap dengan useMemo/useCallback
  • Komputasi simple, child gak memo → langsung tanpa hook

Aturan: profile pertama (React DevTools Profiler), optimisasi setelah ada bukti masalah. Premature optimization = sumber bug.

TL;DR: useMemo(() => value, deps) cache nilai komputasi mahal. useCallback(fn, deps) cache function reference (= useMemo yang return function). Berguna saat: (1) komputasi mahal yang re-run di tiap render, (2) pass ke memo() child untuk skip re-render. Tanpa memo di child, useCallback SIA-SIA. Default: GAK pake — React cepat 99% kasus. React Compiler (R19+) auto-handle, jadi makin sedikit dipake manual.

Yang akan kamu pelajari