React Render Modeli
Önce şunu oku: JavaScript Olay Döngüsü — setTimeout(0) Neden Sonra Yazıyor?
30 saniyede özet
React'in bir bileşeni yeniden çizmesi (render), ekranın değişmesi demek değil. React.memo ise ancak verdiğin değerler her seferinde aynı nesneyse işe yarar; yoksa hiçbir şey kazandırmaz.
Bileşenlerin durmadan yeniden çiziliyor gibi görünüyor. Endişelenmeden önce bilmen gereken bir şey var: yeniden çizmek ekranı değiştirmek demek değil.
-
Bayt: Sayaca bir kez tıkladım, sayfadaki bütün bileşenler yeniden render oldu!
-
Sen: Her yere memo koyalım o zaman.
-
Bayt: Koydum. Hâlâ hepsi render oluyor, üstelik biraz daha yavaş.
-
Bayt: Listeyi baştan yazmak mı pahalı, markete gitmek mi?
Adım adım oku
- State değişince React o bileşeni ve altındaki bütün çocukları yeniden çalıştırır.
- Yeni ağacı öncekiyle karşılaştırır ve gerçekten değişen tek düğümü bulur.
- Commit fazında DOM'a yalnızca o değişiklik yazılır; ekranın geri kalanı olduğu gibi kalır.
- React.memo ile sarılan ve propları aynı kalan bir çocuk, yeniden render edilmeden atlanır.
Listeyi yazmak, markete gitmek
Bir state değiştiğinde React iki ayrı iş yapar:
- Render fazı — bileşen fonksiyonlarını yeniden çalıştırır ve yeni bir element ağacı üretir. Bu faz saf olmalıdır; DOM’a hiçbir şey yazılmaz. React bu fazı iptal edip baştan başlatabilir —
StrictMode’un geliştirmede bileşenleri iki kez çağırmasının sebebi budur. - Commit fazı — yeni ağacı öncekiyle karşılaştırır ve yalnızca gerçekten değişen düğümleri DOM’a yazar.
Yani bir bileşenin “yeniden render olması”, ekranda bir şeyin değiştiği anlamına gelmez. Genellikle hiçbir şey değişmez.
Kafam karıştı, daha basit anlat
Render, alışveriş listesini yeniden yazmaktır. Ekranı güncellemek ise markete gitmektir. React listeyi sık yazar, ama markete yalnızca listede gerçekten bir şey değiştiyse gider.
Kendin gör
Varsayılan ayarda hiç optimizasyon yok. setCount çağır düğmesine bas, sonra adımla ve iki sayıyı karşılaştır: render sayısı ve DOM güncellemesi.
React render modeli — ne yeniden render olur, ne DOM’a yazılır
Tohum 3Appziyaret edilmedihenüz render edilmediHeaderziyaret edilmedihenüz render edilmediSearchBoxziyaret edilmedihenüz render edilmediListziyaret edilmedihenüz render edilmediCounterziyaret edilmedihenüz render edilmediRow 1ziyaret edilmedihenüz render edilmediRow 2ziyaret edilmedihenüz render edilmediRow 3ziyaret edilmedihenüz render edilmediRow 4ziyaret edilmedihenüz render edilmedi
- Render sayısı
- 0
- DOM güncellemesi
- 0
- Boşa render
- 0
Şu an ne oldu?
Bekliyor — setCount çağır
React hiçbir şey yapmaz, ta ki bir state değişene kadar. "setCount çağır" düğmesine basıp render fazını adım adım izle.
Aklında kalsın: Render fazı ile commit fazı ayrı şeylerdir. Bunu ayırt etmek, "neden bu kadar çok render oluyor" sorusunun cevabının ilk yarısıdır.
Olay günlüğü (0)
Henüz olay yok. Oynat veya adımla.
Sırayla dene:
- Hiçbir şey açık değilken. Tüm ağaç render oluyor, DOM’a yalnızca bir güncelleme gidiyor.
Boşa rendersayacı gerisini anlatıyor. - Sadece
React.memo’yu aç.Headeratlanıyor (prop’u sabit bir string), amaSearchBoxveListhâlâ render oluyor. Sarı uyarıyı oku — memoProp'ları değişmediyse bileşenin yeniden render edilmesini atlayan sarmalayıcı. Şartı referans eşitliğidir; inline prop geçiyorsan hiçbir şey yapmaz.Sözlükte gör → çalıştı, karşılaştırma başarısız oldu. useCallback’i de aç.SearchBoxartık atlanıyor.Listhâlâ render oluyor.useMemo’yu da aç.Listve bütün satırlar atlanıyor. Boşa render sıfır.- Satır sayısını 10 yap ve memo’yu kapat. Tek bir sayaç değişikliği on üç bileşeni render ediyor.
Bir bileşenin "render olması" tam olarak ne demek?
Ebeveyn çizilince çocuklar da çizilir
App içindeki sayaç arttı. Altındaki List bileşeninin propları hiç değişmedi. List yeniden render edilir mi? Cevabı göster
Evet. Bir bileşen render edilince altındaki bütün bileşenler de render edilir; React propları kendiliğinden karşılaştırmaz.
Bu, propları değişmese bile geçerlidir. React propları varsayılan olarak karşılaştırmaz — karşılaştırmanın kendisi de bir maliyettir ve çoğu bileşen için render etmek daha ucuzdur.
function App() { const [count, setCount] = useState(0);
// Her render'da YENİ bir dizi ve YENİ bir fonksiyon const items = products.filter((p) => p.active); const onSearch = (q: string) => setQuery(q);
return ( <> <Counter count={count} /> {/* prop gerçekten değişti — doğru render */} <SearchBox onSearch={onSearch} />{/* prop "değişti" — çünkü yeni fonksiyon */} <List items={items} /> {/* prop "değişti" — çünkü yeni dizi */} </> );}Kafam karıştı, daha basit anlat
Ebeveyn yeniden çizilince çocukları da yeniden çizilir, propları değişmemiş olsa bile. Çoğu zaman bu ucuzdur ve dert edilmez.
`useState`'in set fonksiyonunu aynı değerle çağırırsan ne olur?
React.memo’nun tek şartı
React.memo propları yüzeysel (shallow) karşılaştırır, yani referanslarına bakar. {} !== {} ve [] !== [] olduğu için, her render’da yeniden oluşturulan her nesne memo’yu boşa çıkarır.
Doğru kullanım üçünü birlikte gerektirir:
const List = React.memo(function List({ items }: Props) { /* ... */ });
function App() { const items = useMemo(() => products.filter((p) => p.active), [products]);
// Not: setQuery'nin kendisi zaten kararlıdır; sarmalama gereken bu satır değil, // her render'da yeniden oluşan inline fonksiyondur. const onSearch = useCallback((q: string) => setQuery(q), []);
return ( <> <SearchBox onSearch={onSearch} /> <List items={items} /> </> );}Kafam karıştı, daha basit anlat
React.memo “eline aynı kutu mu geldi” diye bakar, kutunun içine bakmaz. Her seferinde yeni bir kutu verirsen, içi aynı olsa bile farklı sanır.
Butona basıldığında `Child` yeniden render olur mu?
Her durumu, `React.memo`'nun işe yarayıp yaramadığına göre ayır.
Ama her yere memo koymak yanlış. Bu dersin en önemli cümlesi bu olabilir: boşa render her zaman bir sorun değildir.
Render ucuzsa — birkaç div, biraz metin — yüz tane boşa render toplamda bir milisaniye bile etmez. useMemo ve useCallback da bedava değildir: bağımlılık dizisi karşılaştırması, ek bellek, ve okunması zorlaşan kod.
Sıralama şu olmalı:
- Ölç. React DevTools Profiler hangi bileşenin gerçekten pahalı olduğunu söyler.
- Yapıyı düzelt. Çoğu zaman state’i aşağı taşımak veya
childrenprop’u kullanmak memo’dan daha iyi çalışır. - Sonra memo. Ve mutlaka üçlü olarak:
memo+useMemo/useCallback.
// State'i yalnızca ihtiyaç duyan bileşene taşı:// artık setCount çağrısı App'i değil, sadece Counter'ı render ederfunction App() { return ( <> <Counter /> {/* count state'i buranın içinde */} <ExpensiveList /> {/* App render olmadığı için hiç etkilenmiyor */} </> );}Aynı fikri bir banka uygulamasında görelim. Hesap ekranında her saniye tıklayan bir oturum sayacı var; bakalım hareket listesi bu tıklardan nasıl etkileniyor.
Derinleş · Hesap hareketleri: sayaç tıklarken liste yerinde durur 4 dosya · ~115 satır · ilk okumada atlayabilirsin
State'i yukarı taşımak (lifting state up) ne zaman performans sorununa dönüşür?
Tuzaklar: anahtar (key)
Liste elemanlarında key olarak dizinin indeksini kullanmak, React’in yanlış elemanı yeniden kullanmasına yol açar. Başa bir eleman eklendiğinde bütün indeksler kayar; React “1 numaralı eleman değişti” sanır ve DOM durumunu (input içeriği, odak, animasyon) yanlış satıra taşır.
{items.map((item, i) => ( <Row key={i} item={item} /> // başa ekleme yapılınca her şey kayar))}
{items.map((item) => ( <Row key={item.id} item={item} /> // kimlik elemanın kendisinden gelir))}Kendini sına
Bir bileşenin yeniden render olması, ekranda bir şeyin değiştiği anlamına gelir.
Bir bileşen yeniden render oldu. Bu ne anlama gelir?
Aklında kalacak üç şey
- 1 İki ayrı adım var: render'da bileşen fonksiyonu yeniden çalışır, commit'te yalnızca gerçekten değişen kısım ekrana yazılır. Render ekran güncellemesi demek değildir.
- 2 Varsayılan davranış: ebeveyn render olunca bütün çocuklar da render olur, propları değişmese bile. Bundan çıkış yolu React.memo'dur.
- 3 memo referans karşılaştırması yapar. Her render'da yeniden oluşan bir dizi ya da fonksiyon memo'yu etkisiz bırakır; useMemo ve useCallback olmadan memo eklemek zarardır.
5 kart sonraki derste seni bekliyor
Bu dersin üstüne kurulanlar
Bunlar bu dersi temel alıyor; hazır olduğunda devam edebilirsin.
- Frontend & EntegrasyonState Nerede Yaşamalı? — Bileşen, Üst Bileşen, Context, Cache, URLHer şeyi tek bir global store'a koydun, düzenli görünüyor. Neden her tuşta bütün ekran yeniden çiziliyor?Derse git
- Frontend & EntegrasyonXSS — Havale Açıklamasına Yazılan Kod Neden Müşterinin Sayfasında Çalışır?Biri sana havale gönderdi, açıklamaya garip bir şey yazdı. Sen sadece hesap hareketlerine baktın. Oturumun nasıl çalındı?Derse git