Tiap kali datanya berubah, React menggambar ulang komponenmu, lalu membandingkan hasil yang baru dengan yang lama supaya tahu bagian halaman mana yang perlu diubah. Proses membandingkan ini namanya reconciliation, dan di sebuah list, React memasangkan item lama dengan item baru lewat key. Bayangin guru yang mencatat muridnya berdasarkan nomor kursi: "kursi 1 suka bola". Begitu ada murid baru duduk di kursi 1 dan yang lain geser satu kursi, catatan itu sekarang menempel ke murid baru. Kalau catatannya ditulis berdasarkan NIS (nomor induk siswa), catatan selalu ikut muridnya. key={index} itu nomor kursi, key={s.id} itu NIS.
🧪 Tebakan cepat: list Andi, Budi, Citra pakai
key={index}, dan tiap baris punya kotak<input>. Kamu mengetik "suka bola" di baris Andi, lalu Dewi ditambahkan di paling atas. Sekarang "suka bola" ada di baris siapa? Tebak dulu, jawabannya ada di bagian "Bugkey={index}: catatan pindah baris".
Render ulang dan reconciliation
- Render artinya React menjalankan function komponen untuk tahu apa yang harus tampil. Hasilnya belum berupa halaman, tapi deskripsi dalam bentuk object JavaScript: "ada
<ul>, isinya tiga<li>". Deskripsi ini sering disebut Virtual DOM. - Waktu datanya berubah (nanti kamu pakai state untuk ini), React render lagi dan mendapat deskripsi baru.
- Reconciliation, sering juga disebut diffing: React membandingkan deskripsi baru dengan yang lama, lalu mengubah DOM asli cuma di bagian yang beda. Elemen yang sama dipakai ulang, bukan dibuat ulang.
Aturan yang dipakai React waktu membandingkan:
- Tipe elemen beda, misalnya
<div>jadi<span>: elemen lama dibuang beserta isinya, lalu dibuat yang baru. - Tipe elemen sama: elemennya dipakai ulang, cuma atribut dan teks yang berubah yang diperbarui.
- Di list, item lama dan baru dipasangkan lewat
key. Key yang sama dipakai ulang, key baru dibuatkan elemen baru, key yang hilang elemennya dibuang.
Elemen yang dipakai ulang membawa semua yang ada di dalamnya: teks yang sudah diketik di <input>, posisi kursor, fokus. Di situlah key jadi penting.
Bug key={index}: catatan pindah baris
function DaftarSiswa({ siswa }) {
return (
<ul>
{siswa.map((s, index) => (
<li key={index}>
{s.nama} <input placeholder="catatan" />
</li>
))}
</ul>
);
}
<input> ini gak diatur oleh React: teks yang kamu ketik disimpan oleh elemen <input> itu sendiri di DOM. Ketik "suka bola" di baris Andi, lalu tambahkan Dewi di paling atas. siswa berubah dari Andi, Budi, Citra jadi Dewi, Andi, Budi, Citra.
| key | Sebelum | Sesudah | <input> yang dipakai |
|---|---|---|---|
| 0 | Andi, berisi "suka bola" | Dewi | input lama key 0: "suka bola" |
| 1 | Budi | Andi | input lama key 1: kosong |
| 2 | Citra | Budi | input lama key 2: kosong |
| 3 | belum ada | Citra | input baru: kosong |
React melihat key 0 masih ada, jadi <li> dan <input> milik key 0 dipakai ulang. Teks namanya diperbarui jadi Dewi, tapi isi kotak input-nya gak disentuh. Hasilnya, "suka bola" muncul di baris Dewi, dan catatan Andi hilang. Itu jawaban tebakannya. Gak ada error, gak ada peringatan: tampilannya salah diam-diam.
Hal yang sama terjadi waktu list diurutkan ulang, difilter, atau ada item yang dihapus dari tengah: nomor urutnya bergeser, isinya pindah ke orang lain.
Pakai id yang stabil
Cukup ganti satu baris:
<li key={s.id}>
{s.nama} <input placeholder="catatan" />
</li>
Datanya { id: 1, nama: "Andi" }, { id: 2, nama: "Budi" }, { id: 3, nama: "Citra" }, lalu Dewi dengan id: 4.
| key | Sebelum | Sesudah | <input> yang dipakai |
|---|---|---|---|
| 4 | belum ada | Dewi | input baru: kosong |
| 1 | Andi, berisi "suka bola" | Andi | input lama key 1: "suka bola" |
| 2 | Budi | Budi | input lama key 2 |
| 3 | Citra | Citra | input lama key 3 |
Key 4 belum pernah ada, jadi cuma Dewi yang dibuatkan elemen baru, disisipkan di atas. Andi, Budi, dan Citra dikenali lewat id-nya, elemennya tetap, dan "suka bola" tetap di baris Andi. Cuma 1 elemen baru yang dibuat, dan gak ada isi yang salah tempat.
Aturan memilih key
- Unik di antara item satu list. Gak perlu unik di seluruh aplikasi.
- Stabil: sama untuk item yang sama di setiap render.
iddari database paling aman. - Jangan
Math.random(): key-nya baru di setiap render, jadi React membuang dan membuat ulang semua elemen. Semua isian hilang, dan kerjanya lebih berat. indexboleh cuma untuk list yang gak pernah ditambah di atas atau tengah, dihapus, atau diurutkan ulang.- Data dibuat di browser dan belum punya id? Bikin id waktu item itu dibuat, misalnya
crypto.randomUUID(), lalu simpan bersama datanya. Jangan bikin di dalammap.
💡 Ganti key untuk mulai dari nol
key juga bisa dipasang di komponen di luar list. Kalau key sebuah komponen berubah, React menganggapnya komponen lain: yang lama dibuang, yang baru dibuat dari nol, termasuk semua isinya.
<FormCatatan key={siswaDipilih.id} siswa={siswaDipilih} />
Pindah dari Andi ke Budi, key berubah dari 1 jadi 2, jadi form dibuat ulang dan kotak isiannya kosong lagi. Tanpa key, form lama dipakai ulang, dan catatan untuk Andi masih tertulis waktu Budi yang dipilih.
⚠️ Jebakan yang sering ketemu
key={index}di list yang bisa berubah. Isi input, fokus, dan hal lain yang disimpan elemen pindah ke item yang salah, tanpa error.key={Math.random()}. Semua elemen dibuat ulang tiap render: isian hilang, dan lebih lambat.- Key kembar. React memperingatkan
Encountered two children with the same key, dan item bisa hilang atau muncul dobel. - Key di elemen yang salah. Key ditaruh di elemen terluar yang dikembalikan
map, misalnya<li key={s.id}>, bukan di<input>di dalamnya. Key juga gak bisa dibaca komponennya sebagai props: kalau butuh id-nya, kirim lewat prop lain, misalnyaid={s.id}.
TL;DR: Tiap data berubah, React render ulang lalu membandingkan deskripsi UI baru (Virtual DOM) dengan yang lama. Proses ini namanya reconciliation, atau diffing, dan hasilnya cuma bagian yang beda yang diubah di DOM. Di list, item dipasangkan lewat key: key sama berarti elemen lama dipakai ulang beserta isinya. key={index} bikin isi input pindah ke baris lain begitu ada item disisipkan di atas, dihapus, atau diurutkan ulang. Pakai key={item.id}: unik di list itu dan stabil. Jangan Math.random(). Mengganti key sebuah komponen membuatnya mulai dari nol.