Pas user hapus item dari shopping cart, React harus tahu MANA item yang dihapus — biar cuma 1 row yang di-remove dari DOM, bukan rebuild seluruh list. Pas user re-order todo list, React harus tahu MANA yang pindah ke mana — biar animasi smooth, state internal item gak ke-mix. Key prop ngasih identitas unik ke tiap list item, dipake React buat mecahkan teka-teki "mana yang sama, mana yang baru, mana yang pindah" pas re-render. Salah pilih key = bug subtle yang bikin pusing pas debug.
💡 Reconciliation = jantung performa React. Tiap render, React bikin Virtual DOM baru, bandingin sama yang lama (diff), update real DOM cuma yang berubah.
keyadalah hint kritis di proses diff list. Salahkey= React rebuild item yang harusnya gak perlu, performa drop.
{items.map(item => (
<li key={item.id}>{item.nama}</li>
))}
Aturan key:
- Unik di antara siblings (gak harus globally unique)
- Stabil — sama untuk item yang sama lintas render
- JANGAN pakai
Math.random()— beda tiap render = React kira item baru terus
Kenapa Index Buruk?
// ❌ Buruk jika list bisa berubah urutan
{items.map((item, index) => <li key={index}>{item}</li>)}
Bayangin list [A, B, C] dengan key index [0, 1, 2]. User hapus A → list jadi [B, C], key [0, 1]. React lihat:
- Position 0 dulu key=0 isi A, sekarang key=0 isi B. "Oh, item key=0 berubah dari A ke B, update DOM."
- Position 1 dulu key=1 isi B, sekarang key=1 isi C. "Update DOM."
- Position 2 dulu key=2 isi C, sekarang key=2 ilang. "Hapus DOM."
Hasil: 2 update + 1 delete = 3 DOM operation, padahal cuma butuh 1 delete. Plus state internal item ke-mix (input value, animation, focus state).
Pakai key={item.id}:
- React lihat item id=A ilang. "Hapus row id=A."
- B + C key sama, posisi sama. "Skip."
Hasil: 1 delete. Performa optimal + state aman.
Reconciliation Process
React bandingin Virtual DOM lama vs baru:
- Bandingkan tipe elemen —
<div>vs<span>= unmount + mount baru (+ semua child!) - Bandingkan props — className/style berubah = update attribute, tipe sama = reuse
- Bandingkan list items by key — match key sama = reuse + update props, mismatched = unmount/mount
State Internal Persist by Key
// Komponen <Counter> punya state internal
function Counter() {
const [count, setCount] = useState(0)
return <button onClick={() => setCount(c + 1)}>Count: {count}</button>
}
// User re-order list:
{items.map(item => <Counter key={item.id} />)} // ✓ count persist per item
{items.map((item, i) => <Counter key={i} />)} // ✗ pas re-order, count ke-shuffle!
Force Re-mount Pakai Key
Kebalikan: kadang kamu MAU komponen reset (state, animation). Trick: ganti key.
// User pindah ke profile lain → reset semua state internal
<UserProfile key={userId} userId={userId} />
Pas userId berubah, key berubah → React unmount lama + mount baru → state fresh.
🎭 Analogi sehari-hari
key di React itu kayak NIK di KTP. Pas data warga di kelurahan di-update, petugas (React) cek NIK biar tahu siapa yang sama, siapa baru, siapa pindah alamat. Tanpa NIK (atau pake nomor antrian yang berubah-ubah = index), petugas kebingungan: "Si Ani yang ini, sama atau beda dari Si Ani kemarin?". Pakai NIK (id stabil), petugas langsung tahu — efisien + akurat. Reconciliation = proses ulasan yang sama: bandingin "warga lama" (Virtual DOM lama) vs "warga sekarang" (Virtual DOM baru), update kelurahan (real DOM) cuma yang berubah.
⚠️ Jebakan yang sering ditemui
key={Math.random()}— beda tiap render, semua item dianggap baru, full re-mount = mahal + state hilang.- Index sebagai key + list bisa berubah — sort/filter/insert bikin index bergeser, state ke-mix.
- Forget
keydi.map()— warning di console + perf turun (React fallback ke index). - Key sama untuk multiple items — duplicate key warning, behavior unpredictable.
- Key pakai
JSON.stringify(item)— works tapi mahal (compute string + comparison). Pakai id field. - Key di outer element wrapper —
<>{items.map(i => <Item key={i.id} />)}</>. Key di Item, BUKAN di Fragment. - Forget Fragment + key — list elements dengan multiple sibling per item → wrap
<React.Fragment key={i.id}>.
🎯 Pilih key apa?
- Data dari database →
item.id✓ (paling umum, stable)- Data dari API tanpa ID stabil →
item.uuidatau combine field unik- Generated client-side →
crypto.randomUUID()saat dibuat, simpan di state- List static yang gak akan berubah urutan →
indexboleh (cuma kalau YAKIN gak ada add/remove/sort)- String content yang unik → string itself (
key={item.email})Aturan: cari nilai STABIL + UNIK per item, gak pernah berubah selama item itu hidup di list.
TL;DR: key = identitas unik per list item, dipake React buat reconciliation (proses diff Virtual DOM). Aturan: unik antar sibling + stabil + jangan random. Index buruk untuk list yang bisa berubah urutan (sort/filter/insert) — state ke-mix. Pakai id dari data. Trick: ganti key untuk force re-mount + reset state. Reconciliation = bandingin tipe element → props → list keys → minimal DOM update.