İki ekip aynı satırı ekler: export default React.memo(ProductCard). Birinde render süresi belirgin biçimde düşer, listede scroll akıcılaşır. Diğerinde hiçbir şey değişmez, hatta Profiler'da render süresi birkaç yüzde artar. Kod satırı aynıdır ama sonuç taban tabana zıttır. Fark React.memo'nun kendisinde değil, hangi bileşene, hangi props profiliyle ve hangi render sıklığında uygulandığındadır.

React.memo bir bileşeni sarar ve önceki props ile yeni props'u yüzeysel olarak karşılaştırır; eşitse render'ı atlar, değilse çalıştırır. Bu mekanizma basittir ama sonucu belirleyen üç değişken vardır: props'ların tipi, karşılaştırmanın maliyeti ve bileşenin kendi render'ının maliyeti. Bu üçü doğru hizaya gelmediğinde memo koruma değil, ek yük üretir.

Aşağıdaki senaryolar bu üç değişkenin birlikte nasıl çalıştığını, hangi kombinasyonda gerçek kazanç doğduğunu ve hangi kombinasyonda memo'nun sessizce zarar verdiğini somut örnekler üzerinden gösteriyor.

İki bileşen aynı optimizasyonu alır, biri hızlanır biri yavaşlar

Bir e-ticaret listesinde 200 ürün kartı render eden ProductCard bileşenini düşünün. Her kart üç primitive prop alıyor: name, price, inStock. Üst bileşen yalnızca sepet sayacı değiştiğinde render oluyor ve kart props'ları arasında hiçbiri değişmiyor. Bu senaryoda React.memo eklemek net kazanç sağlar: karşılaştırma üç primitive değer üzerinde çalışır, maliyeti neredeyse sıfırdır, buna karşılık 200 kartın render'ı atlanır.

Aynı yapıyı bir bildirim panelindeki NotificationItem bileşenine uygulayın; burada üst bileşen saniyede birkaç kez render oluyor ve her seferinde onDismiss fonksiyonu inline tanımlanıyor. Fonksiyon her render'da yeni referans aldığı için memo'nun karşılaştırması her zaman "değişti" sonucu verir. React.memo burada hem çalışmaz hem de her render'da ek bir karşılaştırma adımı ekler; net sonuç küçük ama gerçek bir kayıptır.

İkisi arasındaki tek fark render sıklığı değil, props'un referans kararlılığıdır. Memo'nun işe yarayıp yaramayacağını anlamak için önce şu soru sorulmalı: bu bileşene geçen props, üst bileşen render olduğunda gerçekten aynı mı kalıyor, yoksa yalnızca değeri mi aynı kalıyor?

Aynı örneği bir adım ileri taşıyalım: ProductCard listesinde 200 kart yerine 2.000 kart olsaydı, memo'nun kazancı doğrusal artmazdı; render atlanan her kart için tasarruf edilen süre sabit kalır, ama toplam kart sayısıyla çarpıldığında kazanç büyür. NotificationItem örneğinde ise sorun büyüklükle değil, referans kararsızlığıyla ilgiliydi; bildirim sayısı 5 de olsa 500 de olsa, inline fonksiyon her render'da yeniden üretildiği sürece memo hiçbir kart için işe yaramaz. Bu ayrım, memo kararının "kaç öğe var" sorusundan önce "props gerçekten sabit mi" sorusuna dayanması gerektiğini gösterir.

Prop tipi karşılaştırma maliyetini ve doğruluğunu belirler

Yüzeysel karşılaştırma (Object.is) primitive değerler için hem hızlı hem doğrudur: 5 === 5 her zaman true'dur, ekstra maliyet yoktur. Nesne ve dizi props'larında durum değişir. İki nesne aynı alanlara aynı değerlere sahip olsa bile farklı referanslarsa, yüzeysel karşılaştırma onları "farklı" sayar.

// Değer aynı, referans farklı - memo çalışmaz
function Parent({ items }) {
  const filters = { category: 'elektronik', inStock: true };
  return <FilterPanel filters={filters} items={items} />;
}

// Referans sabitleniyor - memo artık anlamlı
function Parent({ items }) {
  const filters = useMemo(
    () => ({ category: 'elektronik', inStock: true }),
    []
  );
  return <FilterPanel filters={filters} items={items} />;
}

Burada dikkat edilmesi gereken nokta, useMemo eklemenin kendisinin de bir maliyeti olduğudur. Referansı sabitlemek için harcanan iş, memo'nun atladığı render'dan daha pahalıysa, toplam iş azalmaz, yalnızca yer değiştirir. Küçük ve hızlı render alan bileşenlerde bu denge çoğu zaman memo aleyhine döner.

Karşılaştırma maliyeti render maliyetinden büyük olduğunda memo net kayıp üretir

React.memo'nun sarması ücretsiz değildir: her render denemesinde önceki ve yeni props nesnesi karşılaştırılır. Bileşenin kendi render'ı zaten ucuzsa, örneğin tek bir span ve statik bir metin döndürüyorsa, karşılaştırma maliyeti render maliyetine yakınsar veya onu geçer. Bu durumda memo eklemek performansı iyileştirmez, ölçülebilir bir fark üretmeden kod karmaşıklığını artırır.

Çok sayıda prop alan bileşenlerde de aynı risk büyür. On beş prop'u olan bir form alanı bileşeninde her render denemesi on beş ayrı eşitlik kontrolü çalıştırır. Bu kontroller toplandığında, özellikle formun kendisi hafifse, kazanç yerine küçük bir sabit maliyet birikir. React.memo'yu değerlendirirken bileşenin prop sayısını ve her render'ının gerçek maliyetini birlikte hesaba katmak gerekir; yalnızca "render'ı önlemek iyidir" varsayımıyla karar vermek yanıltıcıdır.

Liste öğesi senaryosu: memo'nun en net kazandığı profil

React.memo'nun en tutarlı fayda ürettiği yer, aynı üst bileşenden türeyen ve sayıca çok olan liste öğeleridir. 500 satırlık bir tabloda tek bir hücre güncellendiğinde, memo olmadan 500 satırın tamamı yeniden render olur; memo ile yalnızca değişen satır render olur. Buradaki kazanç doğrusal değil, öğe sayısıyla birlikte büyür.

Bu senaryonun çalışması için satırların anahtar (key) ve props yapısının kararlı olması şarttır. Satıra geçirilen onSelect fonksiyonu her render'da yeniden tanımlanıyorsa, satır sayısı ne kadar büyük olursa olsun memo hiçbir şey kazandırmaz; çünkü karşılaştırma her seferinde "değişti" der. Fonksiyonu useCallback ile sabitlemek veya satıra yalnızca kimlik (id) geçirip fonksiyonu bir üst seviyede tutmak, memo'nun gerçekten devreye girmesini sağlayan ön koşuldur. Bu tür yoğun listelerde DOM boyutunun büyümesiyle birlikte render maliyeti de katlandığından, memo burada yalnızca bir iyileştirme değil, ölçeklenebilirlik önkoşulu haline gelir.

Sık render edilen üst bileşenin durağan alt bileşeni: ikinci kazanç profili

İkinci net fayda profili, üst bileşen sık render olurken alt bileşenin props'unun neredeyse hiç değişmediği durumlardır. Bir dashboard sayfasında saniyede birkaç kez güncellenen bir canlı sayaç, yanında duran ve yalnızca kullanıcı ayarları değiştiğinde güncellenen bir SettingsPanel bileşenini de gereksiz yere render ettirebilir; ikisi aynı ebeveyni paylaşıyorsa. SettingsPanel'i memo ile sarmak, sayaç her tetiklendiğinde bu bileşenin render döngüsünün dışında kalmasını sağlar.

Bu profilde karar verirken üst bileşenin render sıklığı belirleyicidir. Üst bileşen dakikada bir render oluyorsa, alt bileşenin gereksiz render'ı zaten göz ardı edilebilir düzeydedir ve memo eklemek ölçülebilir bir fark yaratmaz. Üst bileşen saniyede onlarca kez render oluyorsa, aynı gereksiz render birikip etkileşim gecikmesine dönüşebilir. Memo'nun değerini render sıklığından bağımsız değerlendirmek, gerçek kazancı gözden kaçırmanın en yaygın yoludur.

Özel karşılaştırma fonksiyonu ne zaman gerekli hale gelir

React.memo ikinci bir parametre olarak özel karşılaştırma fonksiyonu kabul eder. Bu, props içindeki bazı alanların değişimini görmezden gelmek veya derin karşılaştırma yapmak gerektiğinde kullanılır:

function areEqual(prevProps, nextProps) {
  return (
    prevProps.id === nextProps.id &&
    prevProps.status === nextProps.status
    // timestamp gibi sık değişen ama görsel etkisi olmayan
    // alanlar kasıtlı olarak karşılaştırmaya dahil edilmiyor
  );
}

export default React.memo(ActivityRow, areEqual);

Bu yaklaşımın riski açıktır: karşılaştırma fonksiyonu yanlış yazıldığında, gerçekten değişmesi gereken bir prop görmezden gelinir ve arayüz güncel olmayan veri gösterir. Özel karşılaştırma fonksiyonu yalnızca hangi alanların görsel çıktıyı etkilediği net biçimde biliniyorsa yazılmalıdır; aksi halde yüzeysel karşılaştırmanın varsayılan davranışına güvenmek daha güvenlidir. Derin karşılaştırma yazmak yerine, mümkünse üst bileşende props yapısını sadeleştirip yüzeysel karşılaştırmayı yeterli hale getirmek daha sürdürülebilir bir çözümdür.

Derin karşılaştırma (JSON.stringify ile props'u kıyaslamak gibi) bazen "her ihtimale karşı güvenli" bir çözüm gibi görünür, ama maliyeti yüzeysel karşılaştırmadan kat kat yüksektir; nesne büyüdükçe bu maliyet de büyür ve bir noktadan sonra memo'nun kazandırdığından fazlasını geri alır. Nesne içeriği değişken kalacaksa, çözüm genelde karşılaştırmayı derinleştirmek değil, ilgili alanı props'tan çıkarıp bileşene ayrı bir primitive olarak geçirmektir.

Ölçmeden karar vermemek: before/after doğrulaması

React.memo eklemenin gerçekten kazanç sağlayıp sağlamadığı sezgiyle değil, ölçümle doğrulanmalıdır. React DevTools Profiler'da kayıt başlatılır, ilgili etkileşim (scroll, filtre değişimi, veri güncellemesi) tetiklenir ve kayıt durdurulur. Flame graph'ta her bileşenin render süresi ve render sayısı görünür. Memo eklemeden önce ve sonra aynı etkileşim kaydedilip iki profil karşılaştırıldığında, gerçek fark; render sayısındaki düşüş ile toplam commit süresindeki değişim üzerinden net biçimde ortaya çıkar.

Bu doğrulama adımı atlandığında iki tür yanlış karar ortaya çıkar: gerçekten gerekli olan bir memo eklenmez ve liste büyüdükçe performans sorunu birikir, ya da gereksiz yere eklenen memo'lar kod tabanında birikip her birinin küçük karşılaştırma maliyetini toplam render süresine ekler. Büyük React uygulamalarında bundle boyutu gibi metrikler nasıl düzenli izleniyorsa, render performansı da aynı disiplinle, ölçüm bazlı takip edilmelidir.

Memo eklemeden önce sorulacak üç soru

Karar noktasında üç soru pratik bir filtre görevi görür. Birincisi: bu bileşenin üst öğesi ne sıklıkla render oluyor? Nadiren render olan bir üst bileşenin altında memo'nun ölçülebilir bir etkisi olmaz. İkincisi: bu bileşene geçen props'lar üst render olduğunda gerçekten sabit mi kalıyor, yoksa her seferinde yeni referans mı alıyor? Referans kararlılığı sağlanmadan eklenen memo işlevsizdir. Üçüncüsü: bu bileşenin kendi render maliyeti, karşılaştırma maliyetini haklı çıkaracak kadar yüksek mi?

Bu üç sorunun tamamına olumlu yanıt verilmeden eklenen React.memo, çoğu durumda ne render süresini kısaltır ne de kod okunabilirliğini kötüleştirir; sadece görünürde bir önlem gibi durur ve gerçek darboğazın üzerini örter. Render'ı önlemek değil, hangi render'ın gerçekten maliyetli olduğunu bilmek belirleyicidir; bu bilgi olmadan eklenen her optimizasyon, ölçülmemiş bir varsayımdan ibaret kalır.