React.js Rendering Performance
Definisi
Rendering performance di React adalah kemampuan aplikasi untuk memperbarui UI dengan efisien saat state, props, atau context berubah. Tujuannya bukan membuat React “tidak rerender sama sekali”, karena rerender itu normal. Tujuan yang benar adalah memastikan React hanya mengerjakan bagian yang memang perlu diubah, dan tidak membayar biaya tambahan untuk komponen, kalkulasi, atau daftar item yang sebenarnya bisa dipertahankan.
Topik ini biasanya menyentuh lima area yang sering dipakai bersamaan:
memosebagai istilah umum untuk optimisasi yang menahan kerja ulanguseMemountuk menyimpan hasil perhitunganuseCallbackuntuk menyimpan referensi fungsiReact.memountuk mencegah child rerender kalau props-nya stabil- list rendering dan
keyuntuk menjaga identity item saat daftar berubah
Kalau dipakai tepat, hasilnya UI terasa lebih responsif dan kerja render lebih terkontrol. Kalau dipakai asal, optimisasi justru menambah kompleksitas, bikin dependency berantakan, dan kadang tidak memberi manfaat sama sekali.
Kapan Optimisasi Rendering Dipakai
Pakai optimisasi ini kalau ada tanda yang jelas, misalnya:
- daftar item cukup besar dan sering berubah
- ada komponen child yang mahal dirender ulang
- ada kalkulasi berat yang diulang setiap render
- props ke child sering berubah referensi padahal nilainya sama
- interaksi terasa lag saat input, scroll, atau filter
- profiling menunjukkan render berulang di bagian yang sama
Patokan yang sehat: optimisasi harus menjawab masalah yang terukur, bukan sekadar “supaya aman”.
Kapan Justru Overkill
Optimisasi rendering sering menjadi overkill kalau:
- komponen masih kecil dan rerender-nya murah
- masalah performa belum pernah diukur
- kode jadi jauh lebih rumit hanya untuk menghemat kerja yang sangat kecil
useMemoatauuseCallbackdipakai di mana-mana tanpa kebutuhan nyata- dependency array mulai sulit dipelihara
- tim jadi lebih susah membaca alur data karena terlalu banyak wrapper optimisasi
Dalam React, biaya membaca dan merawat kode juga biaya performa. Kalau optimisasi membuat komponen sulit dipahami, itu bisa jadi kerugian yang lebih besar daripada rerender yang sedang dihemat.
Gambaran Besar Alur Render
Yang perlu dipahami dari alur ini:
- React tetap menjalankan render komponen yang terdampak state atau props.
useMemodanuseCallbacktidak menghentikan render; keduanya hanya membantu mempertahankan hasil atau referensi.React.memobekerja di level child component, dengan membandingkan props.keymemengaruhi bagaimana React mengenali identity item dalam list, bukan sekadar label.
useMemo
Definisi
useMemo menyimpan hasil perhitungan agar React tidak menghitung ulang nilai itu pada setiap render, selama dependency belum berubah.
const visibleUsers = useMemo(() => {
return users.filter(user => user.name.includes(query));
}, [users, query]);Kapan dipakai
Gunakan useMemo untuk:
- kalkulasi yang mahal
- filtering atau sorting data besar
- transformasi data yang hasilnya dipakai berulang dalam satu render
- menghasilkan object/array stabil bila referensinya penting untuk child atau dependency lain
Kapan tidak perlu
Jangan pakai useMemo kalau:
- perhitungannya sangat murah
- hasilnya hanya dipakai sekali dan tidak memengaruhi referential equality
- tujuannya cuma “biar rapi”
- dependency justru membuat logika lebih sulit dipahami
Jebakan umum
- Mengira
useMemoadalah cache permanen. Padahal memo bisa dibuang React dalam kondisi tertentu. - Memakai
useMemountuk setiap object literal kecil tanpa bukti kebutuhan. - Menaruh dependency yang tidak lengkap, lalu hasil memo jadi stale.
useCallback
Definisi
useCallback menyimpan referensi fungsi agar identitas function tetap stabil antar render selama dependency tidak berubah.
const handleSelect = useCallback((id) => {
setSelectedId(id);
}, []);Kapan dipakai
Pakai useCallback kalau:
- fungsi dikirim ke child yang dibungkus
React.memo - fungsi dipakai sebagai dependency di
useEffect,useMemo, atau custom hook - identitas function yang stabil memang dibutuhkan untuk mencegah rerender atau effect ulang
Kapan tidak perlu
Kalau fungsi hanya dipakai lokal di komponen yang sama dan tidak diteruskan ke child, useCallback sering tidak memberi keuntungan nyata.
Jebakan umum
- Memakai
useCallbackuntuk semua handler tanpa alasan. - Dependency array terlalu panjang, sehingga callback tetap berubah hampir setiap render.
- Mengandalkan callback stabil untuk menutupi struktur state yang sebenarnya terlalu dekat ke bawah.
React.memo
Definisi
React.memo membungkus component agar React dapat melewati rerender child jika props yang diterima dianggap sama.
const UserRow = React.memo(function UserRow({ user, onSelect }) {
return (
<li>
<button onClick={() => onSelect(user.id)}>{user.name}</button>
</li>
);
});Kapan dipakai
React.memo cocok untuk child component yang:
- sering menerima props yang sama
- mahal dirender ulang
- berada di list besar
- dipanggil berulang dari parent yang sering rerender
Kapan tidak perlu
Jangan gunakan React.memo kalau:
- component sangat kecil dan murah
- props hampir selalu berubah
- parent rerender-nya memang tidak masalah
- custom comparison justru lebih mahal daripada rerender biasa
Jebakan umum
- Mengira
React.memootomatis ampuh. Kalau props mengandung object, array, atau function baru tiap render, memo sering gagal membantu. - Menambahkan custom comparison yang rumit. Comparator yang mahal bisa mengalahkan manfaat skip rerender.
- Menggunakan memo di banyak tempat tanpa memeriksa hasil profiling.
Hubungan useMemo, useCallback, dan React.memo
Ketiga API ini sering dipakai bersama, tapi perannya berbeda:
useMemomenyimpan nilai hasil perhitunganuseCallbackmenyimpan referensi fungsiReact.memomencegah child rerender bila props stabil
Pola umumnya begini:
- Parent menghitung data turunan dengan
useMemo. - Parent membuat handler stabil dengan
useCallback. - Child dibungkus
React.memo. - Child hanya rerender bila props benar-benar berubah.
Kalau salah satu rantai ini putus, optimisasi lain bisa ikut tidak efektif.
List Rendering
Definisi
List rendering adalah proses merender banyak item dari data array ke elemen UI.
{items.map(item => (
<ProductCard key={item.id} item={item} />
))}Kenapa penting untuk performa
List sering menjadi sumber rerender mahal karena:
- jumlah item besar
- tiap item punya subtree cukup kompleks
- parent rerender membuat semua child ikut dievaluasi
- identity item bisa kacau kalau struktur list tidak stabil
Praktik yang baik
- pecah item list menjadi component terpisah
- gunakan
React.memountuk item yang stabil - kirim props seminimal mungkin
- hindari membuat object/function baru di dalam
mapkalau bisa dipindahkan atau distabilkan
Jebakan umum
- Menaruh semua logika item langsung di dalam
map. - Mengoper props besar yang berubah terus walau isinya sama.
- Memakai index sebagai
keyuntuk list yang bisa berubah urutan.
Key Stability
Definisi
key adalah identitas stabil untuk item list. React memakainya untuk mencocokkan item lama dan baru saat daftar berubah.
Kenapa stabilitas key penting
Kalau key stabil, React bisa:
- mempertahankan state internal item yang sama
- melakukan update minimum saat item ditambah, dihapus, atau dipindah
- menghindari unmount/mount yang tidak perlu
Kalau key tidak stabil, React bisa salah mengira item lama sebagai item baru. Akibatnya:
- state lokal item bisa pindah ke baris yang salah
- input di list bisa lompat nilainya
- animasi dan fokus bisa terganggu
- rerender terasa mahal karena identity berubah terus
Key yang baik
Pilih key yang:
- unik
- stabil
- berasal dari identity data, misalnya
id
{todos.map(todo => (
<TodoItem key={todo.id} todo={todo} />
))}Key yang buruk
Hindari:
- index sebagai key untuk list yang bisa reorder
- key acak seperti
Math.random() - key yang dibangun dari nilai yang sering berubah
// Hindari
{items.map((item, index) => (
<Row key={index} item={item} />
))}Jebakan Umum yang Membuat Rerender Mahal
1. Props referensial selalu baru
Object, array, dan function yang dibuat inline akan punya referensi baru setiap render.
<Child options={{ dense: true }} onClick={() => handleClick(id)} />Kalau Child memakai React.memo, props seperti ini bisa membuat memo gagal efektif.
2. State ditempatkan terlalu tinggi
Kalau state diletakkan di parent yang besar, satu perubahan kecil bisa memicu rerender subtree yang luas.
3. Turunan data dihitung ulang di banyak tempat
Filtering atau sorting yang sama sebaiknya tidak diulang berkali-kali di level komponen yang berbeda kalau hasilnya bisa dipusatkan.
4. Key tidak stabil
Index key, random key, atau key yang berubah karena urutan data akan membuat React kehilangan identity item.
5. Comparator memo terlalu mahal
Custom comparison di React.memo yang terlalu dalam atau terlalu banyak operasi bisa lebih mahal daripada rerender biasa.
6. Optimisasi tanpa profiling
Mengubah banyak komponen menjadi memoized tanpa bukti sering berakhir pada kode yang lebih rumit tapi tidak lebih cepat.
Contoh Pola yang Sehat
import { memo, useCallback, useMemo, useState } from 'react';
const UserRow = memo(function UserRow({ user, onSelect }) {
return (
<li>
<button onClick={() => onSelect(user.id)}>{user.name}</button>
</li>
);
});
export default function UserList({ users }) {
const [query, setQuery] = useState('');
const visibleUsers = useMemo(() => {
return users.filter(user =>
user.name.toLowerCase().includes(query.toLowerCase())
);
}, [users, query]);
const handleSelect = useCallback((id) => {
console.log('selected', id);
}, []);
return (
<>
<input value={query} onChange={e => setQuery(e.target.value)} />
<ul>
{visibleUsers.map(user => (
<UserRow key={user.id} user={user} onSelect={handleSelect} />
))}
</ul>
</>
);
}Contoh ini masuk akal karena:
- hasil filter disimpan dengan
useMemo - handler seleksi stabil dengan
useCallback - item list dibungkus
React.memo keymemakaiuser.id, bukan index
Cara Menentukan Perlu Optimisasi atau Tidak
Pakai alur keputusan sederhana ini:
Pertanyaan paling penting bukan “bisa dioptimalkan atau tidak”, melainkan:
- apakah masalahnya nyata
- apakah optimisasi membuat kode lebih jelas atau justru lebih sulit dirawat
- apakah biaya tambahan dari memoization lebih kecil daripada manfaatnya
Catatan Implementasi
- Mulai dari struktur state yang baik sebelum menambahkan optimisasi.
- Gunakan profiling tools untuk melihat komponen mana yang benar-benar sering rerender.
- Optimisasi paling efektif biasanya datang dari perbaikan struktur data, pemecahan component, dan key yang stabil.
- Jangan mengandalkan satu teknik untuk semua kasus;
useMemo,useCallback, danReact.memopunya fungsi berbeda. - Simpan optimisasi di tempat yang jelas alasannya, supaya tim lain tidak bingung kenapa komponen itu dibungkus memo.
Referensi Terkait
- Hooks Dasar React.js: useState, useEffect, useMemo, useCallback, dan useRef
- Component, Props, State, dan Render Flow di React.js
- Forms React.js: Controlled vs Uncontrolled, Lifting State, Validation, dan Submit Flow
Ringkasan Cepat
useMemountuk hasil perhitungan.useCallbackuntuk referensi fungsi.React.memountuk child component yang props-nya stabil.keyharus stabil dan berasal dari identity data.- Optimisasi dipakai kalau ada masalah nyata, bukan sebagai default.
- Kalau terlalu dini dioptimalkan, kode biasanya lebih sulit dirawat daripada lebih cepat.