React'te bir liste render etmek için bir dizi üzerinde map() çağır ve her öğe için bir JSX parçası döndür. Liste değiştiğinde React'in öğeleri birbirinden ayırabilmesi için her öğenin en dıştaki öğesine o öğeyi tanımlayan bir değerle, genellikle ID'siyle, bir key prop'u ver.
Diziye { id: 'dat', name: 'Date', price: 4 } ekle; dördüncü bir satır belirir. JSX'in kendine ait bir döngü sözdizimi yoktur: map bir öğe dizisi döndürür ve React bir diziyi her öğesini sırayla render ederek render eder.
map ile dizileri render etmek
map fonksiyonunu her öğe için bir kez çağırır ve sonuçları yeni bir dizide toplar. JSX süslü parantezlerinin içinde bu dizi sadece bir değerdir, bu yüzden yukarıdaki gibi doğrudan işaretlemede map'leyebilir ya da önce diziyi oluşturabilirsin:
const items = fruits.map((fruit) => <li key={fruit.id}>{fruit.name}</li>);
return <ul>{items}</ul>;
İki ayrıntı insanları tökezletir. Ok fonksiyonunda süslü parantez kullanırsan bir return'e ihtiyacın olur: fruits.map((f) => { return <li key={f.id}>{f.name}</li>; }). O olmadan fonksiyon undefined döndürür ve liste boş kalır. Ayrıca key, içindeki bir öğeye değil map'ten döndürülen öğeye konur. Öğe bir bileşense key'i bileşene koy: <FruitRow key={fruit.id} fruit={fruit} />.
Önce filter, sonra map
Öğelerin bir kısmını göstermek için önce diziyi filtrele, sonra sonucu map'le. Her metot tek bir iş yapar ve zincir "eşleşen meyveler, liste öğeleri olarak" cümlesi gibi okunur.
Arama kutusuna er yaz; liste Eraser, Ruler ve Stapler'a daralır; In stock only'yi işaretle, Ruler da gider. Filtrelenmiş liste render sırasında iki state parçasından hesaplanır, bu yüzden senkronda tutulacak ikinci bir dizi state'te yoktur. visible.length === 0 &&, &&'nin güvenli bir kullanımıdır, çünkü karşılaştırma gerçek bir boolean'dır (koşullu render sayfası çıplak bir 0 ile ne olduğunu gösteriyor).
Key'ler ne yapar
Bir liste yeniden render edildiğinde React yeni öğe dizisini öncekiyle karşılaştırır. Onları eşleştirme yolu key'lerdir: şu an 3 key'ine sahip öğe, yer değiştirmiş olsa bile önceki 3 key'li öğedir. React o öğenin DOM düğümünü ve bileşen state'ini tutar, yalnızca değişeni taşır ya da günceller. Yeni key'li öğeler oluşturulur, kaybolan key'ler kaldırılır.
Key'ler olmadan React'in eşleştirebileceği tek şey konumdur. Hiçbir şey yer değiştirmediği sürece bu sorun değil, bir şey yer değiştirdiği anda ise yanlıştır.
Index key'ler neden bozulur
map index'i ikinci argüman olarak aktarır, bu yüzden key={index} cazip gelir. Index bir öğeyi değil, bir konumu tarif eder. En üste bir öğe ekle; her öğenin index'i bir kayar, böylece React 0. öğenin hâlâ 0. öğe olduğuna inanır ve eski state'ini orada tutar.
Aşağıdaki demo aynı listeyi iki kez render eder: bir kez index'e, bir kez ID'ye göre key'lenmiş. Her satırda kendi metnini tutan bir input var.
İki listede de Ada'nın yanına math yaz, sonra Add to top'a tıkla. ID listesinde not Ada'yla kalır. Index listesinde not yeni kişiye atlar, çünkü ilk input hâlâ 0. konumdaki input'tur ve 0. konum artık başka biridir. İsimler iki listede de doğrudur, çünkü veriden gelirler; yalnızca DOM'da ve bileşenlerde yaşayan state yanlış gider. Gerçek uygulamalar bununla onay kutularında, odakta, animasyonlarda ve useState tutan her bileşende karşılaşır.
Index key'ler yalnızca bunların hepsi geçerli olduğunda güvenlidir: liste hiç yeniden sıralanmıyor ya da filtrelenmiyor, öğeler sonu dışında hiç eklenmiyor ya da çıkarılmıyor ve öğelerin kendine ait bir state'i yok. Statik bir alt bilgi bağlantıları listesi buna uyar. Kullanıcının düzenleyebildiği herhangi bir şey genellikle uymaz.
Key'ler nereden gelir
En iyi key, veride zaten bulunan bir ID'dir: bir veritabanı ID'si, bir ürün SKU'su, bir slug, bir kullanıcı adı. Sunucudan gelen verinin neredeyse her zaman bir tane vardır.
Tarayıcıda oluşturulan öğeler için her öğeye oluşturduğunda bir ID ver ve öğeyle birlikte sakla: yukarıdaki demodaki gibi bir sayaç ya da crypto.randomUUID(). Key'i asla render sırasında üretme:
// 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 }]);
}
Her render'da değişen bir key, index'ten daha kötüdür: her öğe her seferinde yok edilip yeniden oluşturulur, sen yazarken input'lar odağı kaybeder ve hiçbir şey state'ini korumaz. Aynı numara bilerek kullanıldığında işe yarar: bir bileşenin key'ini değiştirmek onu sıfırlar; useState sayfası bir formu temizlemek için bunu kullanıyor.
Key'ler yalnızca kardeşler arasında benzersizdir
Bir key'in tüm uygulamada değil, tek bir liste içinde benzersiz olması gerekir. İki ayrı liste aynı ID'leri yeniden kullanabilir, çünkü React key'leri yalnızca aynı ebeveynin çocukları arasında karşılaştırır.
İki bölümde de 1 ve 2 key'li yemekler var ve bu doğru: her ul kendi listesidir. Sorun tek bir liste içindeki yinelenen key'lerdir: React çakışan öğeleri birbirinden ayıramaz ve liste güncellendiğinde onları çoğaltabilir ya da düşürebilir.
Bir öğe <dt> ve <dd> gibi birkaç kardeş öğe render ettiğinde onları <Fragment key={item.id}> ile sar. Kısa <> sözdizimi bir key alamaz.
"unique key prop" uyarısı
Key'i atlarsan React geliştirme sırasında tarayıcı konsoluna şunu yazdırır:
Each child in a list should have a unique "key" prop.
Check the render method of `App`.
İkinci satır, listeyi üreten render'ın sahibi olan bileşeni adlandırır; bu da hangi map'i düzelteceğini söyler. Aynı key'e sahip iki kardeş farklı bir mesaj alır; mesaj "Encountered two children with the same key" ile başlar. Bu sayfadaki önizleme bu kontrolü atlayan bir production derlemesi gibi çalışır, bu yüzden bir key'i silsen bile uyarıyı burada görmezsin. Yine de ilk örnekte birini sil: liste yine render edilir, çünkü eksik bir key bir çökme değil, bir doğruluk riskidir. Uyarıyı düzeltmek için map'ten döndürülen öğeye sabit bir key ekle; key={index} eklemek onu susturur ama yukarıda gösterilen hatayı geri getirir.
Şunu da unutma: key bileşenine aktarılmaz. FruitRow içinde props.key undefined'dır. Bileşenin ID'ye ihtiyacı varsa onu normal bir prop olarak ikinci kez aktar: <FruitRow key={f.id} id={f.id} />.
Sıkça Sorulan Sorular
React liste öğelerinde neden key ister?
Key'ler, render'lar arasında hangi öğenin hangisi olduğunu React'e söyler. Dizi değiştiğinde React eski ve yeni öğeleri key'e göre eşleştirir; böylece konuma göre tahmin yürütmek yerine her öğenin state'ini ve DOM düğümünü doğru veriyle tutabilir.
Dizi index'ini key olarak kullanabilir miyim?
Yalnızca sırası hiç değişmeyen ve ortasına hiç öğe eklenmeyen ya da çıkarılmayan bir liste için. Öğeler yer değiştirebiliyorsa bir index key state'i konuma bağlar, böylece yazılan metin, onay kutuları ve odak yanlış öğede kalır.
Key olarak ne kullanmalıyım?
Veriye ait bir ID: bir veritabanı ID'si, bir ürün SKU'su, bir kullanıcı adı. Tarayıcıda oluşturulan öğeler için, öğeyi oluşturduğunda bir kez bir ID üret (bir sayaç ya da crypto.randomUUID()), asla render sırasında değil.
Key'ler tüm uygulamada benzersiz olmak zorunda mı?
Hayır. Key'lerin yalnızca aynı listedeki kardeşler arasında benzersiz olması gerekir. İki farklı liste aynı key'leri kullanabilir.
Bir bileşen kendi key'ini okuyabilir mi?
Hayır. key React tarafından kullanılır ve bileşene prop olarak aktarılmaz. Bileşenin ID'ye ihtiyacı varsa onu id={item.id} gibi başka bir adla yeniden aktar.
"Each child in a list should have a unique key prop" uyarısını nasıl düzeltirim?
map'ten döndürdüğün en dıştaki öğeye, öğenin sabit bir ID'sini kullanarak bir key ekle. O öğe bir fragment ise <Fragment key={id}> kullan, çünkü <> bir key alamaz.