Bayangkan Anda sedang membangun aplikasi React yang mulai terasa “berat”: daftar yang panjang, komponen yang kompleks, dan setiap interaksi kecil terasa sedikit terlambat. Kode masih berjalan, tapi responsivitas perlahan memudar. Di titik inilah optimasi bukan lagi pilihan, melainkan kebutuhan.
Dalam ekosistem React, ada tiga “alat bantu” yang sering disebut ketika berbicara tentang performa: React.memo, useCallback, dan useMemo. Ketiganya tidak akan secara ajaib membuat aplikasi menjadi super cepat, tetapi digunakan dengan tepat, mereka mampu mengurangi render yang tidak perlu, menstabilkan referensi fungsi, dan menghindari perhitungan ulang yang mahal.
Artikel ini akan mengupas bagaimana cara kerja ketiganya, kapan sebaiknya digunakan, dan kapan justru harus dihindari. Alih-alih sekadar menghafal “pakem” penggunaan hook, kita akan mencoba memahami pola pikir di balik optimasi React-agar Anda bisa memutuskan sendiri kapan sebuah optimasi benar-benar membawa manfaat, dan kapan itu hanya menambah kompleksitas tanpa alasan yang kuat.
Memahami akar masalah re-render di React sebelum menyentuh Memo useCallback dan useMemo
Sering kali masalah performa di React bukan karena kodenya “salah”, tapi karena komponen terlalu sering dirender ulang tanpa alasan yang kuat. Setiap kali state berubah, atau parent mengirim props baru (meski nilainya sama secara logika), React akan mencoba merender ulang anak-anaknya. Di sinilah hal-hal kecil seperti inline function, objek baru di props, atau context yang terlalu gemuk bisa jadi biang kerok. Akhirnya, halaman terasa “berat” padahal secara visual hampir tidak ada yang berubah. Lebih gawat lagi, kita sering panik langsung lempar optimization hook ke sana-sini tanpa tahu dulu pola re-render mana yang sebenarnya bikin lambat.
Sebelum bicara soal trik dan “senjata” optimasi, yang perlu dipahami adalah: apa saja pemicu re-render dan komponen mana yang benar-benar butuh dioptimasi. Biasanya, tanda-tandanya bisa kamu rasakan saat mengembangkan:
- Input terasa delay saat pengguna mengetik.
- Animasi patah-patah ketika ada update kecil di UI.
- Scroll jadi tersendat ketika list panjang ikut dirender ulang.
- Debugger menunjukkan banyak komponen ikut merender ulang tanpa perubahan visual berarti.
| Pemicu | Dampak |
|---|---|
| Perubahan state global | Banyak komponen ikut render |
| Props berupa fungsi/objek baru | Child dianggap berubah terus |
| Perhitungan berat di render() | UI terasa lag setiap update |
Strategi praktis menggunakan React Memo untuk mengunci komponen presentasional yang boros render
Langkah pertama yang paling masuk akal adalah mengidentifikasi komponen yang benar-benar “boros” sebelum dikunci dengan React.memo. Biasanya ini adalah komponen presentasional yang menerima props besar (misalnya data tabel, list panjang, atau objek konfigurasi kompleks) dan dipakai berulang di banyak tempat. Kamu bisa mengombinasikan profiling di React DevTools dengan sedikit intuisi: cari komponen yang sering di-render ulang padahal prop-nya tidak banyak berubah. Setelah ketemu, bungkus komponen tersebut dengan React.memo lalu pastikan props-nya stabil (misalnya dengan useCallback dan useMemo di komponen induk) supaya mekanisme shallow comparison yang dipakai React benar-benar bekerja. Hindari refleks membungkus semua komponen; fokus di yang visualnya berat, sering render, dan punya dampak langsung ke performa.
- Prioritaskan komponen list, card, dan tabel besar.
- Pastikan props tidak selalu berubah referensinya setiap render.
- Uji sebelum-sesudah dengan React Profiler, bukan pakai feeling saja.
- Satukan logika berat di induk, kirim hasil akhirnya sebagai props “jadi”.
| Komponen | Cocok pakai React.memo? | Alasan Singkat |
|---|---|---|
ProductCard |
Ya | Sering dipakai di grid, UI cukup kompleks. |
ButtonPrimary |
Mungkin | Hanya kalau dipakai ratusan kali di satu halaman. |
UserListItem |
Ya | Muncul berulang di list panjang. |
LayoutWrapper |
Tidak | Jarang re-render, efek ke performa minim. |
Dalam praktiknya, pattern paling rapi adalah memisahkan komponen jadi dua: container yang stateful dan presentational yang “dikunci”. Container mengurus data, fetching, dan callback yang sudah di-memo dengan useCallback, lalu menyuplai hasil olahan ke komponen presentasional yang dibungkus React.memo. Dengan cara ini, meskipun data global berubah, komponen visual yang berat tidak ikut bergetar setiap saat. Untuk kasus yang sangat sensitif, kamu bahkan bisa menulis areEqual kustom di React.memo untuk mengontrol logika perbandingan prop secara lebih granular, misalnya hanya peduli pada beberapa field penting saja.
- Container: pegang state, side-effect, dan logika bisnis.
- Presentational: fokus tampilan, dibungkus
React.memo. - Callback: stabilkan dengan
useCallbackagar tidak memicu re-render sia-sia. - Data berat: wrap dengan
useMemosebelum dikirim sebagai props.
“`jsx
// Container: logic + memoized props
const ProductListContainer = () => {
const [products, setProducts] = useState([]);
const handleSelect = useCallback((id) => {
// …logic pilih produk
}, []);
const visibleProducts = useMemo(
() => products.filter((p) => p.stock > 0),
[products]
);
return (
);
};
// Presentational: terkunci dengan React.memo
const ProductListView = React.memo(function ProductListView({ products, onSelect }) {
return (
))}
);
});
“`
Merancang dependensi useCallback dan useMemo agar stabil tanpa terjebak optimasi berlebihan
Inti dari stabilnya memoization di React sebenarnya ada di cara kita menyusun daftar dependensi. Kuncinya: hanya masukkan hal-hal yang benar-benar dibutuhkan, tapi jangan sampai mengorbankan kebenaran data. Kalau sebuah callback memakai user.id dan filters, ya kedua itu wajib ada di array dependensinya. Yang sering bikin kacau adalah ketika kita memasukkan objek atau fungsi baru yang selalu dibuat ulang di setiap render, padahal nilainya secara logika tidak berubah. Di sinilah kita bisa “merapikan” state dan props, misalnya dengan memecah objek besar jadi beberapa state primitif, atau mengangkat pembuatan fungsi ke luar komponen jika tidak butuh akses ke scope internal.
- Prioritaskan kejelasan dulu, baru mikir optimasi.
- Gunakan
useCallbackdanuseMemosaat benar-benar ada rerender mahal. - Stabilkan referensi dengan meminimalkan pembuatan objek/fungsi baru.
- Jangan takut menambah dependensi; justru yang berbahaya itu mengurangi secara paksa.
| Situasi | Strategi Dependensi |
|---|---|
| Callback sering berubah tanpa alasan jelas | Pastikan hanya state/props yang dipakai yang jadi dependensi |
| Objek di dependensi selalu bikin re-render | Pecah objek jadi state primitif atau memoize objeknya |
| Optimasi terasa berlebihan | Hapus memoization yang tidak memberi dampak nyata |
const filters = useMemo(() => ({
status,
keyword,
}), [status, keyword]);
const fetchData = useCallback(() => {
api.getList(filters);
}, [filters]);
Pola dan anti pola umum dalam optimasi performa React beserta rekomendasi penerapannya di proyek nyata
Di proyek nyata, pola optimasi yang sehat biasanya berangkat dari kebutuhan, bukan dari sekadar keinginan “biar kelihatan canggih”. Beberapa kebiasaan yang layak dijadikan pegangan misalnya: gunakan memoization secara selektif di komponen yang memang sering re-render dan punya perhitungan berat, pastikan dependency array di useCallback dan useMemo selalu akurat, dan biasakan memecah UI menjadi komponen kecil yang jelas tanggung jawabnya. Di sisi lain, ada juga anti pola yang sering bikin performa justru turun, seperti membungkus hampir semua komponen dengan React.memo, membuat callback dengan useCallback tapi tidak pernah dikonsumsi oleh komponen turunan, atau memakai useMemo untuk operasi yang sepele dan tidak mahal. Pola yang baik cenderung membuat kode tetap mudah dibaca, sedangkan anti pola sering meninggalkan jejak “optimasi palsu” yang bikin orang lain (atau diri sendiri di masa depan) kebingungan.
- Direkomendasikan: fokus optimasi di bagian yang terbukti bottleneck lewat profiling.
- Direkomendasikan: gunakan
React.memo,useCallback, danuseMemodi boundary komponen yang sering menerima props kompleks. - Hindari: menebar memoization di mana-mana tanpa mengukur dampaknya.
- Hindari: mengorbankan keterbacaan hanya demi menghilangkan satu-dua re-render yang sebenarnya tidak mahal.
| Pola | Anti Pola | Catatan Praktis |
|---|---|---|
| Profiling dulu | Optimasi “feeling” | Gunakan React Profiler sebelum refactor besar |
| Memo secukupnya | Semua komponen di-memo |
Pilih komponen berat atau sering berubah |
| Callback stabil | useCallback tanpa kebutuhan |
Pakai saat dikirim ke child yang di-memo |
| useMemo untuk hitungan mahal | useMemo untuk operasi ringan |
Jangan menambah kompleksitas tanpa keuntungan nyata |
Closing Remarks
Pada akhirnya, mengoptimalkan React dengan memo, useCallback, dan useMemo bukan soal “menghafal tiga hook sakti”, tetapi soal memahami kapan dan mengapa kita menggunakannya.
Alih-alih menaburkan ketiganya di setiap sudut kode, jauh lebih penting untuk membaca alur komponen, mengenali pola yang boros render, lalu memilih alat yang tepat untuk mengatasinya. Ada kalanya menulis kode yang sedikit lebih “biasa” justru menghasilkan performa yang cukup, dan ada kalanya satu lapis memoization bisa memangkas beban besar pada aplikasi yang kompleks.
Jadi, lain kali Anda mendapati komponen yang terus merender ulang, daftar yang terasa berat, atau logika mahal yang berulang-ulang dipanggil, Anda sudah punya tiga kandidat solusi di kotak peralatan: React.memo untuk menahan render yang tak perlu, useCallback untuk menstabilkan fungsi, dan useMemo untuk mengunci perhitungan yang mahal.
Sisanya adalah eksperimen dan pengukuran: profil, uji, ulangi. Di situ letak seni mengoptimalkan React-bukan hanya membuat aplikasi berjalan, tetapi membuatnya berjalan dengan ringan, efisien, dan tetap menyenangkan untuk dikembangkan.

