Ana içeriğe geç
Blog'a Dön
ReactPerformanceJavaScriptFrontend
Ağaç (Tamamlanmış)

React’te Performans Üzerine: Shallow ve Deep Karşılaştırmanın Gerçek Yüzü

2025-10-226 dk

React’te performans konusu çoğu geliştiricinin bir noktada yüzleştiği bir mesele. Genelde hikâye şöyle başlar:
“Memo koydum ama işe yaramadı.”
Bu durumun altında yatan sebep basit: React’ın bir bileşeni neden yeniden çizdiğini anlamadan yapılan her optimizasyon, kağıt üzerinde doğru görünse bile pratikte bir fayda sağlamaz.

Bu yazıda, senelerdir aynı hataların farklı takımlarda tekrarlandığını gördüğüm iki temel konuyu ele alıyorum: shallow comparison ve deep comparison. Eğer bu ikisini içselleştirirsen, performans optimizasyonu React tarafında artık sisli bir alan olmaktan çıkar.


Objelerin Aynı Olup Olmadığına Kim Karar Veriyor?

JavaScript, primitif tipleri değerlerine göre karşılaştırırken, objeleri referans üzerinden kıyaslar. Bu davranış çoğu performans probleminin sessizce doğduğu yerdir.

"selam" === "selam"   // true
{} === {}             // false
[] === []             // false

İki obje aynı görünebilir; ancak aynı değillerse React onları farklı kabul eder.
React.memo kullanıp hâlâ gereksiz render almanın nedeni tam da budur.


Neden “React.memo” Her Şeyi Kurtarmıyor?

Basit bir örnek üzerinden konuşalım. Parent her render olduğunda yeni bir obje oluşturduğun bir senaryoyu düşün:

function Parent() {
  const style = { color: "red" };
  return <Child style={style} />;
}

const Child = React.memo(({ style }) => {
  return <div style={style}>Child</div>;
});

Kâğıt üzerinde Child’ın tekrar render olmaması gerekiyor.
Ama olur. Çünkü style her render’da yenilenir; yani React açısından prop değişmiştir.

Bu, sahada en çok karşılaştığım performans tuzağıdır. Kod temiz görünür, niyet düzgündür; ama sonuç yine de yanlıştır.


useMemo ve useCallback: Gereksiz Romantizm Değil, Referans Yönetimi

Bu iki hook yıllarca “performans arttırıcı” diye pazarlanıp durdu.
Asıl amaç çok daha nettir: Referansı sabitlemek.

const style = useMemo(() => ({ color: "red" }), []);

Bu ifade React’e şunu söyler:

“Bu obje değişmediği sürece bana aynı referansı ver, yeni adres üretme.”

Fonksiyonlarda da durum aynıdır:

const handleClick = useCallback(() => {
  console.log("clicked");
}, []);

Büyük bileşen ağaçlarında zincirleme render’ları durdurmak için çoğu zaman bu kadar basit bir dokunuş yeter.


Gerçekten Kullanılması Gereken Yerler

Bu bölüm, yıllardır performans problemi çözmüş biri olarak çıkardığım berrak özettir:

  • Ağaç içinde derine giden bileşenlere obje ya da fonksiyon prop olarak veriyorsan,
  • Büyük veri listelerini işliyorsan,
  • useEffect içinde obje/fonksiyon dependency olarak durmak zorundaysa,
  • Hesaplama maliyeti yüksek bir operasyonun sonucunu saklamak istiyorsan.

Bunların dışındaki çoğu kullanım, gereksiz karmaşa üretir.
React’te performans, “her yere memo atıp rahatlamak” değildir; az hareket eden bir bileşen ağacı oluşturmaktır.


Deep Comparison: Her Şeyi Ölçmeye Çalışmanın Bedeli

Shallow comparison bazı durumlarda yetmez. Objelerin içeriği aynı ama referanslar farklı olabilir. İnsan ister istemez “o zaman derin kıyas yapalım” fikrine kapılır.

React bunu varsayılan olarak yapmaz.
İstersen React.memoya özel bir karşılaştırıcı verebilirsin:

const Child = React.memo(
  ({ user }) => <div>{user.name}</div>,
  (prev, next) => JSON.stringify(prev.user) === JSON.stringify(next.user)
);

Bu yapılabilir; ama unutmamak gerekir:

  • JSON.stringify ucuz değildir,
  • Büyük objelerde beklenmedik gecikmeler çıkarabilir,
  • Gerçek çözüm çoğu zaman doğru veri modeli kurmaktır.

Eğer state’i immutable ilkelere göre tasarlarsan, deep comparison ihtiyacı neredeyse ortadan kalkar.


Son Söz: Render Neden Olduğunu Anlamadan Render’ı Engelleyemezsin

React’ın nasıl karar verdiğini anlamak, performans optimizasyonunun gerçek başlangıç noktasıdır. Shallow ve deep comparison konularını çözdüğünde, gereksiz render’ların nereden geldiğini artık sezgisel olarak fark etmeye başlarsın.

Bağlantı Haritası

Mühendislik
Tarih
Anlatılar
Graph Loading...