Merhaba arkadaşlar, bugün başımı çok ağrıtan ve belki de birçok React geliştiricisinin farkında olmadan yaptığı bir hatadan bahsedeceğim. Uzun süre, listeleri render ederken `key` prop'u olarak kolaycılığa kaçıp dizinin `index` değerini kullanıyordum. "Ne olacak ki, çalışıyor işte!" diyordum. Ta ki, listemden bir öğeyi sildiğimde veya sıralamasını değiştirdiğimde, component'lerin state'inin tamamen karıştığını, input alanlarının yanlış satırlara kaydığını ve performansın düştüğünü görene kadar. İşte o zaman kafayı yemiştim!
Sorun Tam Olarak Neydi?
Projemde, kullanıcıların ekleyip silebileceği dinamik bir "Görev Listesi" (todo list) vardı. Her görev item'ı kendi içinde bir checkbox ve bir text input'u barındırıyordu. Listeyi render etmek için şu klasik ve masum görünen kodu kullanıyordum:
Her şey harika çalışıyor gibiydi. Ta ki, listenin ortasından bir öğe sildiğimde veya sıralamayı değiştirdiğimde... Mesela, "Alışverişe git" yazan 2. sıradaki görevi sildiğimde, ondan sonraki tüm öğelerin `key` değeri bir kaydı. React, `key`'i 2 olan component'in artık "Faturaları öde" görevini temsil ettiğini düşündü! Bu da, "Faturaları öde" satırındaki checkbox state'inin, aslında silinen "Alışverişe git" satırına ait olan state ile devam etmesine neden oldu. Yani, yanlış görevler işaretleniyor, input'lara yazılanlar yanlış yerlere gidiyordu. Kabus gibiydi!
Doğru Çözüm: Benzersiz ve Stabil Key'ler
Sorunun özü şu: `key`, React'in bir listedeki her bir elemanı benzersiz ve zaman içinde sabit bir şekilde tanımlaması için kullandığı bir ipucudur. Index kullanmak, bu elemanın kimliğini değil, sadece o anki konumunu (sırasını) belirtir. Konum değişince, React karıştırır.
İşte benim kullandığım en temiz çözüm: Her liste öğesinin, verinin kendisinden gelen gerçekten benzersiz ve değişmeyen bir ID'si[/COLOR] olmalı. Bu genellikle back-end'den gelen bir `id` alanıdır (UUID, database primary key gibi).
Eğer veriniz böyle bir ID içermiyorsa (örneğin, lokal, kullanıcının anlık oluşturduğu bir liste), o zaman veriyi oluştururken veya component'e almadan hemen önce bir ID üretmelisiniz. `crypto.randomUUID()` (modern tarayıcılarda) veya `uuid` gibi kütüphaneler bu iş için biçilmiş kaftan. Asla `Math.random()` kullanmayın, çünkü her render'da değişir ve bu da performans kaybına yol açar!
Kazanımlar ve Performans Artışı
Bu basit değişikliği yaptıktan sonra neler oldu biliyor musunuz?
State karışıklığı tamamen ortadan kalktı. Silme veya sıralama işlemlerinde input'lar ve checkbox'lar artık sadık kalıyor.
React, her bir elemanı `key` sayesinde doğru bir şekilde tanıyabildiği için, gereksiz re-render'lar ve DOM güncellemeleri azaldı. Sadece değişen, eklenen veya kaldırılan elemanlar etkileniyor. Bu, özellikle büyük listelerde inanılmaz bir performans avantajı sağlıyor.
Sonuç olarak, `map` fonksiyonundaki `index` parametresi, sadece başka bir iş için (örneğin sıra numarası göstermek) kullanılmalı. Asla ama asla `key` prop'u olarak kullanılmamalı.
Peki ya siz? React'te listeleme yaparken bu hataya düştünüz mü ve ne gibi ilginç bug'larla karşılaştınız? Ya da `key` için farklı, ilginç stratejileriniz var mı? Yorumlarda paylaşalım!
Projemde, kullanıcıların ekleyip silebileceği dinamik bir "Görev Listesi" (todo list) vardı. Her görev item'ı kendi içinde bir checkbox ve bir text input'u barındırıyordu. Listeyi render etmek için şu klasik ve masum görünen kodu kullanıyordum:
JavaScript:
{todos.map((todo, index) => (
<TodoItem
key={index} // İşte BU BÜYÜK HATA!
todo={todo}
onToggle={toggleTodo}
onDelete={deleteTodo}
/>
))}
Her şey harika çalışıyor gibiydi. Ta ki, listenin ortasından bir öğe sildiğimde veya sıralamayı değiştirdiğimde... Mesela, "Alışverişe git" yazan 2. sıradaki görevi sildiğimde, ondan sonraki tüm öğelerin `key` değeri bir kaydı. React, `key`'i 2 olan component'in artık "Faturaları öde" görevini temsil ettiğini düşündü! Bu da, "Faturaları öde" satırındaki checkbox state'inin, aslında silinen "Alışverişe git" satırına ait olan state ile devam etmesine neden oldu. Yani, yanlış görevler işaretleniyor, input'lara yazılanlar yanlış yerlere gidiyordu. Kabus gibiydi!
Sorunun özü şu: `key`, React'in bir listedeki her bir elemanı benzersiz ve zaman içinde sabit bir şekilde tanımlaması için kullandığı bir ipucudur. Index kullanmak, bu elemanın kimliğini değil, sadece o anki konumunu (sırasını) belirtir. Konum değişince, React karıştırır.
İşte benim kullandığım en temiz çözüm: Her liste öğesinin, verinin kendisinden gelen gerçekten benzersiz ve değişmeyen bir ID'si[/COLOR] olmalı. Bu genellikle back-end'den gelen bir `id` alanıdır (UUID, database primary key gibi).
JavaScript:
// Her todo objesinin benzersiz bir `id` alanı olduğunu varsayalım.
const initialTodos = [
{ id: 'a1b2c3', text: 'Kahve al', completed: false },
{ id: 'd4e5f6', text: 'React öğren', completed: true },
{ id: 'g7h8i9', text: 'Makale yaz', completed: false },
];
// Artık key olarak index DEĞİL, o öğenin kendi id'sini kullanıyoruz.
{todos.map((todo) => (
<TodoItem
key={todo.id} // 🎉 İşte bu! Değişmeyen, benzersiz kimlik.
todo={todo}
onToggle={toggleTodo}
onDelete={deleteTodo}
/>
))}
Eğer veriniz böyle bir ID içermiyorsa (örneğin, lokal, kullanıcının anlık oluşturduğu bir liste), o zaman veriyi oluştururken veya component'e almadan hemen önce bir ID üretmelisiniz. `crypto.randomUUID()` (modern tarayıcılarda) veya `uuid` gibi kütüphaneler bu iş için biçilmiş kaftan. Asla `Math.random()` kullanmayın, çünkü her render'da değişir ve bu da performans kaybına yol açar!
Bu basit değişikliği yaptıktan sonra neler oldu biliyor musunuz?
State karışıklığı tamamen ortadan kalktı. Silme veya sıralama işlemlerinde input'lar ve checkbox'lar artık sadık kalıyor.
React, her bir elemanı `key` sayesinde doğru bir şekilde tanıyabildiği için, gereksiz re-render'lar ve DOM güncellemeleri azaldı. Sadece değişen, eklenen veya kaldırılan elemanlar etkileniyor. Bu, özellikle büyük listelerde inanılmaz bir performans avantajı sağlıyor.
Sonuç olarak, `map` fonksiyonundaki `index` parametresi, sadece başka bir iş için (örneğin sıra numarası göstermek) kullanılmalı. Asla ama asla `key` prop'u olarak kullanılmamalı.
Peki ya siz? React'te listeleme yaparken bu hataya düştünüz mü ve ne gibi ilginç bug'larla karşılaştınız? Ya da `key` için farklı, ilginç stratejileriniz var mı? Yorumlarda paylaşalım!