Default React: parent re-render → SEMUA child ikut re-render, walaupun props mereka gak berubah. Kebanyakan kasus ini OK (React cepat). Tapi pas ada child yang RENDER-nya mahal — misalnya komponen tabel 1000 row, chart kompleks — re-render sia-sia ngabisin frame budget. React.memo = wrap komponen jadi "hanya re-render kalau props beneran berubah" (pakai shallow comparison). Kombinasi memo + useCallback (untuk function props) + useMemo (untuk object/array props) = trio optimisasi yang harus dipake bareng biar efektif.
💡 Salah satu = sia-sia. Pakai
memodoang? Function/object prop baru tiap render →memogagal compare → tetap re-render. PakaiuseCallbackdoang? Child gak di-memo → tetep re-render karena parent. Tiganya saling tergantung. Kalau salah pakai = tambah complexity tanpa benefit.
const ExpensiveList = memo(function ExpensiveList({ items }) {
console.log("List dirender")
return (
<ul>
{items.map(item => <li key={item}>{item}</li>)}
</ul>
)
})
function App() {
const [count, setCount] = useState(0)
const items = useMemo(() => ["A", "B", "C"], []) // ← stable reference!
return (
<div>
<button onClick={() => setCount(c => c + 1)}>Count: {count}</button>
<ExpensiveList items={items} /> {/* gak re-render saat count++ */}
</div>
)
}
Klik tombol → count berubah → App re-render. Tapi ExpensiveList SKIP re-render karena items reference sama (thanks to useMemo).
Cara Kerja memo
- Saat parent re-render, React bandingkan props baru vs lama (shallow comparison)
- Props sama (reference identik untuk object/array, value sama untuk primitive) → SKIP re-render
- Props berbeda → re-render seperti biasa
Gotcha: Object/Array/Function Props
// ✗ BUG — object literal beda reference tiap render = memo gagal
function App() {
return <ExpensiveList items={["A", "B"]} /> // array baru tiap render!
}
// ✗ BUG — function inline juga beda reference
function App() {
return <ExpensiveList onItemClick={() => console.log("click")} />
}
// ✓ FIX — useMemo + useCallback
function App() {
const items = useMemo(() => ["A", "B"], [])
const handleClick = useCallback(() => console.log("click"), [])
return <ExpensiveList items={items} onItemClick={handleClick} />
}
Custom Comparison Function
// Default shallow compare — kalau gak cukup, custom
const ExpensiveItem = memo(
function ExpensiveItem({ user }) { /* ... */ },
(prev, next) => prev.user.id === next.user.id // hanya compare ID
)
Return true = props sama (skip re-render), false = beda (re-render).
🎭 Analogi sehari-hari
memo itu kayak resepsionis hotel yang inget tamu lama. Tamu pulang → checkout → resepsionis catat data. Pas tamu balik 2 minggu kemudian, resepsionis cek catatan: "data sama persis kayak dulu? Kasih kunci kamar lama, gak perlu register ulang (skip re-render)." Tapi kalau tamu ganti nomor HP/alamat (props berubah), resepsionis register ulang dari awal (re-render). Untuk hotel kecil dengan 5 tamu/hari, register ulang gak masalah (default React cukup cepat). Untuk hotel besar dengan 1000 tamu/hari, optimisasi check-in (memo + reference stability) bikin antrian gak panjang (frame budget gak abis).
⚠️ Jebakan yang sering ditemui
- Pakai
memotanpauseCallback/useMemodi parent — function/object prop beda reference tiap render =memogagal. WAJIB combo. memoeverywhere — overhead compare props bisa lebih mahal dari render itself untuk komponen ringan. Cuma untuk komponen yang BENERAN expensive.- Wrap component dengan banyak children —
<MemoizedLayout>{children}</MemoizedLayout>— children itself = JSX object baru tiap render.memogagal. - Forget destructure di custom comparison —
(prev, next) => prev === nextselalu false untuk object props (reference baru). Compare specific fields. - Memo komponen yang state-driven internal — kalau komponen sering re-render karena state internal,
memogak bantu (internal re-render gak peduli props). - Deep object props — shallow compare gak cek nested.
prev.user.profile.name === next.user.profile.namedi custom comparison.
🎯 Wrap dengan
memoatau biarin?
- Default: GAK pakai memo. React cukup cepat.
- Component render BANYAK item (table 100+ row) →
memo✓- Component pakai library berat internally (chart, video player) →
memo✓- Component yang sering jadi child component dengan state berubah cepat →
memo+useCallbackdi parent- Component sederhana (50-100 elements) → biarin, gak butuh memo
- List item dengan banyak prop →
memoper item bisa drastis improve perfAturan: profile dulu pake React DevTools, optimisasi setelah ada bukti. React Compiler (R19+) auto-handle, jadi memo manual makin jarang.
TL;DR: memo(Component) = wrap komponen jadi skip re-render kalau props gak berubah (shallow compare). WAJIB combo dengan useCallback + useMemo di parent untuk function/object props (kalau gak, reference baru tiap render = memo gagal). Cuma berguna untuk komponen yang RENDER-nya mahal. Default React cepat — pakai memo cuma setelah profiler nunjukin masalah. R19 + React Compiler bisa auto-handle ini.