Menu

List dan Key di React: map(), Prop key, Key dari Index

Untuk merender list di React, kamu memanggil map() pada array dan mengembalikan JSX untuk setiap item, lalu memberi setiap item prop key. Pelajari filter lalu map, dari mana key sebaiknya berasal, dan kenapa key dari index array membuat item tertukar saat list berubah.

Halaman ini dilengkapi editor yang bisa dijalankan. Edit, jalankan, dan lihat output-nya seketika.

Untuk merender list di React, panggil map() pada array dan kembalikan sepotong JSX untuk setiap item. Beri elemen terluar setiap item prop key dengan nilai yang mengidentifikasi item itu, biasanya ID-nya, agar React bisa membedakan item-item itu saat list berubah.

Tambahkan { id: 'dat', name: 'Date', price: 4 } ke array, dan baris keempat muncul. JSX tidak punya sintaks loop sendiri: map mengembalikan array berisi elemen, dan React merender array dengan merender setiap elemennya secara berurutan.

Merender array dengan map

map memanggil fungsimu sekali untuk setiap item dan mengumpulkan hasilnya ke array baru. Di dalam kurung kurawal JSX, array itu hanyalah sebuah nilai, jadi kamu bisa melakukan map langsung di markup, seperti di atas, atau membuat array-nya lebih dulu:

const items = fruits.map((fruit) => <li key={fruit.id}>{fruit.name}</li>);
return <ul>{items}</ul>;

Ada dua detail yang sering menjebak. Jika kamu memakai kurung kurawal di arrow function, kamu butuh return: fruits.map((f) => { return <li key={f.id}>{f.name}</li>; }). Tanpa itu, fungsinya mengembalikan undefined dan list-nya kosong. Lalu, key ditaruh di elemen yang dikembalikan dari map, bukan di elemen di dalamnya. Jika item-nya berupa komponen, taruh key di komponen itu: <FruitRow key={fruit.id} fruit={fruit} />.

Filter, lalu map

Untuk menampilkan sebagian item, filter array-nya lebih dulu lalu lakukan map pada hasilnya. Setiap method melakukan satu tugas, dan rangkaiannya terbaca seperti kalimat "buah yang cocok, sebagai item list".

Ketik er di kotak pencarian, dan list menyempit menjadi Eraser, Ruler, dan Stapler; centang In stock only, dan Ruler ikut hilang. List hasil filter dihitung saat render dari dua state tersebut, jadi tidak ada array kedua di state yang harus dijaga agar tetap sinkron. visible.length === 0 && adalah pemakaian && yang aman karena perbandingannya menghasilkan boolean sungguhan (halaman conditional rendering menunjukkan apa yang terjadi dengan 0 biasa).

Apa yang dilakukan key

Saat sebuah list dirender ulang, React membandingkan array elemen yang baru dengan yang sebelumnya. Key adalah cara React mencocokkannya: elemen dengan key 3 sekarang adalah elemen dengan key 3 sebelumnya, meskipun posisinya pindah. React mempertahankan node DOM dan state komponen milik item itu, dan hanya memindahkan atau memperbarui yang berubah. Item dengan key baru dibuat, dan key yang hilang dihapus.

Tanpa key, satu-satunya yang bisa dipakai React untuk mencocokkan adalah posisi. Itu tidak masalah selama tidak ada yang berpindah, dan langsung salah begitu ada yang berpindah.

Kenapa key dari index rusak

map mengirim index sebagai argumen keduanya, jadi key={index} terasa menggoda. Index menggambarkan posisi, bukan item. Sisipkan sebuah item di paling atas, dan index setiap item bergeser satu, sehingga React mengira item 0 masih item 0 dan mempertahankan state lamanya di sana.

Demo di bawah merender list yang sama dua kali, sekali dengan key dari index dan sekali dengan ID. Setiap baris punya input yang menyimpan teksnya sendiri.

Ketik math di sebelah Ada di kedua list, lalu klik Add to top. Di list ber-ID, catatannya tetap bersama Ada. Di list ber-index, catatannya melompat ke orang baru, karena input pertama tetaplah input di posisi 0, dan posisi 0 sekarang milik orang lain. Nama-namanya benar di kedua list, karena berasal dari data; hanya state yang tinggal di DOM dan di komponen yang menjadi salah. Aplikasi sungguhan mengalami ini pada checkbox, fokus, animasi, dan komponen apa pun yang menyimpan useState.

Key dari index hanya aman jika semua syarat ini terpenuhi: list-nya tidak pernah diurutkan ulang atau difilter, item tidak pernah disisipkan atau dihapus kecuali di akhir, dan item-itemnya tidak punya state sendiri. List link footer yang statis memenuhi syarat. Apa pun yang bisa diedit pengguna biasanya tidak.

Dari mana key berasal

Key terbaik adalah ID yang sudah ada di data: ID database, SKU produk, slug, username. Data dari server hampir selalu punya ID.

Untuk item yang dibuat di browser, beri setiap item sebuah ID saat item dibuat dan simpan bersama item itu: counter, seperti di demo di atas, atau crypto.randomUUID(). Jangan pernah membuat key saat render:

// Wrong: a new key on every render, so React remounts every item every time
{todos.map((todo) => <Todo key={Math.random()} todo={todo} />)}

// Right: the ID is created once, in the event that adds the item
function addTodo(text) {
    setTodos([...todos, { id: crypto.randomUUID(), text }]);
}

Key yang berubah di setiap render lebih buruk daripada index: setiap item dihancurkan dan dibuat ulang setiap kali, input kehilangan fokus selagi kamu mengetik, dan tidak ada yang mempertahankan state-nya. Trik yang sama justru berguna jika disengaja: mengubah key sebuah komponen akan mengatur ulang komponen itu, yang dipakai halaman useState untuk mengosongkan form.

Key hanya unik di antara sibling

Sebuah key harus unik di dalam satu list, bukan di seluruh aplikasi. Dua list yang terpisah boleh memakai ulang ID yang sama, karena React hanya membandingkan key di antara anak dari induk yang sama.

Kedua bagian punya hidangan dengan key 1 dan 2, dan itu benar: setiap ul adalah list-nya sendiri. Key ganda di dalam satu list-lah masalahnya: React tidak bisa membedakan item-item yang bentrok, dan saat list diperbarui, React bisa menggandakan atau membuangnya.

Saat satu item merender beberapa elemen sibling, seperti <dt> dan <dd>, bungkus dengan <Fragment key={item.id}>. Sintaks pendek <> tidak bisa menerima key.

Peringatan "unique key prop"

Jika kamu tidak memberi key, React mencetak ini di konsol browser saat development:

Each child in a list should have a unique "key" prop.

Check the render method of `App`.

Baris kedua menyebutkan komponen yang render-nya menghasilkan list itu, yang memberi tahu map mana yang perlu diperbaiki. Dua sibling dengan key yang sama mendapat pesan yang berbeda, diawali "Encountered two children with the same key". Pratinjau di halaman ini berjalan seperti build production, yang melewatkan pemeriksaan itu, jadi kamu tidak akan melihat peringatannya di sini meskipun kamu menghapus sebuah key. Tetap hapus satu di contoh pertama: list-nya tetap dirender, karena key yang hilang adalah risiko kebenaran, bukan crash. Untuk memperbaiki peringatan itu, tambahkan key yang stabil ke elemen yang dikembalikan dari map; menambahkan key={index} memang membungkam peringatannya, tetapi memunculkan kembali bug yang ditunjukkan di atas.

Perhatikan juga bahwa key tidak dikirim ke komponenmu. Di dalam FruitRow, props.key bernilai undefined. Jika komponen itu butuh ID-nya, kirim sekali lagi sebagai prop biasa: <FruitRow key={f.id} id={f.id} />.

Pertanyaan yang sering diajukan

Kenapa React butuh key pada item list?

Key memberi tahu React item mana adalah item yang mana di antara render. Saat array berubah, React mencocokkan item lama dan baru berdasarkan key, sehingga bisa mempertahankan state dan node DOM setiap item bersama data yang benar, alih-alih menebak berdasarkan posisi.

Bolehkah saya memakai index array sebagai key?

Hanya untuk list yang urutannya tidak pernah berubah dan tidak pernah ada item yang disisipkan atau dihapus di tengahnya. Jika item bisa berpindah, key dari index mengikat state ke posisi, sehingga teks yang diketik, checkbox, dan fokus berakhir di item yang salah.

Apa yang sebaiknya dipakai sebagai key?

ID yang menjadi bagian dari data: ID database, SKU produk, username. Untuk item yang dibuat di browser, buat ID sekali saat item dibuat (counter atau crypto.randomUUID()), jangan pernah saat render.

Apakah key harus unik di seluruh aplikasi?

Tidak. Key hanya perlu unik di antara sibling dalam list yang sama. Dua list yang berbeda boleh memakai key yang sama.

Bisakah sebuah komponen membaca key-nya sendiri?

Tidak. key dipakai oleh React dan tidak dikirim ke komponen sebagai prop. Jika komponen itu butuh ID-nya, kirim lagi dengan nama lain, seperti id={item.id}.

Bagaimana cara memperbaiki "Each child in a list should have a unique key prop"?

Tambahkan key ke elemen terluar yang kamu kembalikan dari map, dengan ID stabil dari item itu. Jika elemen itu fragment, pakai <Fragment key={id}>, karena <> tidak bisa menerima key.

Ilustrasi bahasa pemrograman di Coddy

Belajar coding dengan Coddy

Mulai