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/useCallbackeverywhere — itu memory leak + cognitive overhead. Pakai HANYA setelah profiler nunjukin masalah konkret. React 19 + React Compiler malah otomatis handle ini, jadiuseMemo/useCallbackmanual 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 EVERYWHERE — anti-pattern. Memory + cognitive overhead tanpa benefit. Profile dulu.
- Lupa deps array —
useMemo(() => fn(x))(no deps) = re-compute tiap render = sama aja kayak gak pake useMemo. Wajib deps. - Object/array di deps —
useMemo(..., [{x: 1}])= beda reference tiap render = re-compute terus. useMemountuk komputasi murah —useMemo(() => a + b, [a, b])= overhead caching > komputasi sendiri. Hindari.useCallbacktanpa memo child — gak ada efek. Cuma berguna sebagai prop kememo()child.- Reference equality bug di deps —
[obj.x](primitive) ✓ vs[obj](reference, beda tiap render) ✗. - useMemo untuk side effect —
useMemo(() => { fetch() }, [...])SALAH. useMemo buat compute nilai. Side effect = useEffect.
🎯 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 →useMemodi 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.