Dinamik Key Üretimi

Önceki modülde React key'lerini öğrendik. .map ile veri üzerinde iterasyon yaparken, React'in render'lar arasında hangi DOM işlemlerini tetikleyeceğini bilmesi için her elemana benzersiz bir key prop'u vermemiz gerekiyor.

Önceki örneklerde verimiz zaten benzersiz bir id alanıyla geliyordu:

const stok = [
  { id: "abc123", isim: "Yumuşak yumurta kalıbı" },
  { id: "def456", isim: "Hello Kitty diş fırçası" },
];

stok.map((urun) => <StokKarti key={urun.id} urun={urun} />);

Peki ya verimizde kullanabileceğimiz benzersiz bir alan yoksa?

Bu, geliştiricilerin en sık karşılaştığı sorulardan biridir. React her eleman için benzersiz bir değer ister, ama biz her zaman bunu veremeyiz!


Sticker Örneği

Tıklandığında rastgele çıkartmalar (sticker) ekleyen bir uygulama düşünelim. Her tıklamada:

  1. Rastgele bir çıkartma seçiliyor
  2. Kullanıcının cursor konumu (X/Y) ekleniyor
  3. Yeni çıkartma, spread operatörü ile stickers dizisine ekleniyor (mutasyonu önlemek için)
// StickerPad.js
import React from "react";
import { getSticker } from "./Stickers.data";

function StickerPad() {
  const [stickers, setStickers] = React.useState([]);

  return (
    <button
      onClick={(event) => {
        const stickerData = getSticker();
        const newSticker = {
          ...stickerData,
          x: event.clientX,
          y: event.clientY,
        };
        const nextStickers = [...stickers, newSticker];
        setStickers(nextStickers);
      }}
    >
      {stickers.map((sticker, index) => (
        <img
          key={index} // ⚠️ Şimdilik index kullanıyoruz
          src={sticker.src}
          alt={sticker.alt}
          style={{
            left: sticker.x,
            top: sticker.y,
            width: sticker.width,
            height: sticker.height,
          }}
        />
      ))}
    </button>
  );
}

Henüz doğru bir key atamadık. Ne kullanmalıyız?


Neden Basit Çözümler Çalışmaz?

src kullanamayız

src alanını key olarak kullansak ne olur? Çıkartma sayısı sınırlı ama kullanıcı sınırsız çıkartma ekleyebilir — aynı çıkartmadan birden fazla eklendiğinde key çakışması yaşanır.

Bileşik key de yetmez

src + x + y gibi tüm alanları birleştirerek "bileşik" bir key oluşturmaya çalışsak bile, kullanıcı aynı konuma aynı çıkartmayı iki kez ekleyebilir. Yine çakışma!

Math.random() kötü bir fikir

// ❌ Görünürde çalışıyor ama performans felaketi!
{stickers.map((sticker) => (
  <img key={Math.random()} ... />
))}

Bu yaklaşım konsol uyarısını giderir, ama çok ciddi bir sorun yaratır:

Her yeni çıkartma eklendiğinde bileşen yeniden render edilir. Math.random() her render'da çalıştığı için mevcut tüm çıkartmaların key'i değişir. React bir elemanın key'i değiştiğinde onu tamamen yeni bir eleman olarak kabul eder ve sıfırdan oluşturur.

Yani 10 çıkartma varken 11. eklediğimizde, React 10 resmi silip 11 yeni resim oluşturur — sadece 1 yeni resim oluşturmak yerine! DOM işlemleri JavaScript'in en yavaş operasyonlarından biridir, bu yüzden bunu minimize etmek istiyoruz.


Doğru Çözüm: Oluşturulurken ID Atamak

Fikir doğru — her çıkartmaya rastgele bir değer vermek. Sorun ise bu değeri her render'da yeniden üretmek.

Çözüm: ID'yi render sırasında değil, çıkartma oluşturulurken — yani onClick içinde — atamak:

onClick={(event) => {
  const stickerData = getSticker();
  const newSticker = {
    ...stickerData,
    x: event.clientX,
    y: event.clientY,
    id: crypto.randomUUID(), // ✅ Bir kez oluştur, sakla
  };
  const nextStickers = [...stickers, newSticker];
  setStickers(nextStickers);
}}

Artık ID, çıkartma nesnesinin kalıcı bir parçası. Her render'da yeniden üretilmiyor. Bunu key olarak kullanabiliriz:

{
  stickers.map((sticker) => (
    <img
      key={sticker.id} // ✅ Kararlı ve benzersiz
      src={sticker.src}
      alt={sticker.alt}
      style={{
        left: sticker.x,
        top: sticker.y,
        width: sticker.width,
        height: sticker.height,
      }}
    />
  ));
}

Benzersiz ID Üretme Yöntemleri

İki yaygın yöntem var:

// Yöntem 1: Math.random()
// Küçük ondalıklı sayı üretir: 0.7391234...
id: Math.random();

// Yöntem 2: crypto.randomUUID()
// Daha uzun, alfanümerik bir değer üretir:
// "550e8400-e29b-41d4-a716-446655440000"
id: crypto.randomUUID();

crypto.randomUUID(), çakışma ihtimali astronomik ölçüde düşük olduğu için tercih edilen yöntemdir. Her iki tarayıcıda da geniş destek görür.


Özet

YaklaşımSorun
key={index}Eleman sırası değişirse yanlış davranış
key={sticker.src}Aynı çıkartmadan birden fazla olunca çakışır
key={Math.random()} (render'da)Her render'da tüm elemanlar yeniden oluşturulur
key={sticker.id} (onClick'te atanan)✅ Kararlı, benzersiz, performanslı

Altın kural: Key'ler render sırasında üretilmemeli, veri oluşturulurken kalıcı olarak atanmalıdır.

Farklı bir çözüm buldunuz mu?

Bu problemi kendiniz çözmeye çalıştıysanız muhtemelen farklı bir yola gittiniz. Daha basit görünen pek çok alternatif çözümün belirli durumlarda ciddi dezavantajları var: performans sorunlarına ya da arayüz hatalarına yol açabilirler. Bir sonraki derste bu yaygın alternatifleri ve risklerini inceleyeceğiz.


React ile Bağlantısı

Bu ders aslında birkaç önceki konuyu bir araya getiriyor:

Tüm bu kavramlar gerçek bir uygulama senaryosunda nasıl birlikte çalıştığını görmek için güzel bir örnek! 🚀