React Automatic Batching: Kaç Yeniden Render Edilir?
React uygulamalarınızın beklenenden daha yavaş çalıştığını mı fark ettiniz? Belki de kullanıcı arayüzünüzdeki ufak bir etkileşim, arka planda gereğinden fazla “yeniden render” (re-render) işlemine yol açıyor olabilir. Bu durum, özellikle büyük ve dinamik uygulamalarda ciddi performans sorunlarına davetiye çıkarabilir. Ancak endişelenmeyin, React’in “Otomatik Batching” mekanizması tam da bu noktada devreye giriyor ve performansı artırmak için gizli bir kahraman gibi çalışıyor. Peki, bu sihirli özellik tam olarak nedir ve uygulamalarınızda kaç yeniden render işlemi gerçekleşmesini sağlar?
Modern web uygulamaları, kullanıcı deneyimini zenginleştirmek adına sürekli etkileşim ve dinamik güncellemelerle doludur. Bir butona tıklama, bir form alanına veri girme veya sunucudan veri çekme gibi basit görünen eylemler bile, arkaplanda karmaşık bir dizi işlemin tetiklenmesine neden olabilir. React, bu karmaşıklığı yönetmek için harika bir soyutlama katmanı sunsa da, her state güncellemesinin anında DOM’u etkilemesi durumunda uygulamalarımız hızla yavaşlayabilir.
Bir React bileşenindeki state değiştiğinde, React varsayılan olarak bu bileşeni ve ilgili alt bileşenlerini yeniden render eder. Bu yeniden render işlemi, sanal DOM’un yeniden oluşturulması ve gerçek DOM ile arasındaki farkların bulunup güncellenmesi anlamına gelir. Eğer aynı anda birden fazla state güncellemesi yaparsak ve her biri ayrı ayrı bir yeniden render döngüsünü tetiklerse, bu durum gereksiz hesaplamalara ve dolayısıyla performans düşüşüne yol açar. İşte bu noktada “batching” devreye girer. Batching, birden fazla state güncellemesini tek bir yeniden render döngüsünde birleştirerek, React’in bu güncellemeleri daha verimli bir şekilde işlemesini sağlar. Bu sayede, uygulamanızın daha akıcı ve tepkisel kalmasına yardımcı olurken, kullanıcıya daha iyi bir deneyim sunarız.
Bu makalede, React’in otomatik batching mekanizmasını derinlemesine inceleyecek, React 18 ile gelen yenilikleri ele alacak ve bu özelliğin uygulamalarınızın performansını nasıl kökten değiştirebileceğini pratik örneklerle göstereceğiz. Ayrıca, otomatik batching’in ne zaman çalıştığını, ne zaman çalışmadığını ve özel senaryolarda performansı daha da optimize etmek için neler yapabileceğimizi keşfedeceğiz. Hazırsanız, React’in yeniden render dünyasına adım atalım ve batching’in gizemlerini çözelim!
Automatic Batching Nedir ve Neden Hayati Önem Taşır?
React uygulamalarında performans optimizasyonunun temel taşlarından biri olan “batching”, en basit tanımıyla, birden fazla state güncelleme işlemini tek bir yeniden render işlemi altında birleştirme prensibidir. Peki, neden bu kadar önemli? Gelin, konuyu biraz daha derinlemesine inceleyelim.
React’in Render Süreci Nasıl İşler?
React, bileşen tabanlı bir mimariye sahiptir ve uygulamanızdaki her görsel parçayı bir bileşen olarak ele alır. Bir bileşenin state’i veya props’ları değiştiğinde, React bu değişikliği algılar ve aşağıdaki adımları izleyerek kullanıcı arayüzünü günceller:
- State veya Props Değişikliği: Bir
useStatekancası ile state’i güncellediğinizde veya üst bileşenden gelen props’lar değiştiğinde tetiklenir. - Yeniden Render Çağrısı: React, ilgili bileşenin render fonksiyonunu yeniden çalıştırır. Bu, bileşenin güncel state ve props değerleriyle yeni bir “sanal DOM” ağacı oluşturması anlamına gelir.
- Diffing (Fark Bulma): React, yeni oluşan sanal DOM ağacı ile bir önceki sanal DOM ağacını karşılaştırır. Bu işleme “reconciliation” veya “diffing” denir. React, yalnızca değişen kısımları tespit eder.
- DOM Güncellemesi: Farklılıklar bulunduğunda, React bu değişiklikleri en verimli şekilde gerçek DOM’a yansıtır. Bu, tarayıcının ekranı yeniden çizmesine (repaint) ve düzeni yeniden hesaplamasına (reflow) neden olabilir.
Her bir state güncellemesi bu adımları ayrı ayrı tetiklerse, özellikle eş zamanlı veya birbirini takip eden birden fazla güncelleme olduğunda, gereksiz ve pahalı DOM manipülasyonları yaşanır. İşte bu durum, uygulamaların tepkisizleşmesine ve yavaşlamasına neden olan temel faktörlerden biridir.
Batching Olmasaydı Ne Olurdu?
Batching mekanizması olmasaydı, React uygulamalarımızdaki her setState çağrısı, anında bir yeniden render döngüsünü tetiklerdi. Hayal edin ki bir formunuz var ve kullanıcı bir butona tıkladığında üç farklı state’i (örneğin, isLoading, error, data) aynı anda güncellemeniz gerekiyor. Batching olmadan bu senaryo şöyle işlerdi:
setIsLoading(true)çağrılır -> React yeniden render eder (1. render).setError(null)çağrılır -> React yeniden render eder (2. render).setData(newData)çağrılır -> React yeniden render eder (3. render).
Bu durumda, tek bir kullanıcı eylemi için üç ayrı yeniden render işlemi gerçekleşir ve her biri sanal DOM’u yeniden oluşturup gerçek DOM’u güncellerdi. Bu, özellikle karmaşık bileşen ağaçlarına sahip uygulamalarda büyük bir performans yükü oluşturur ve CPU kullanımını artırır. Kullanıcı, bu mikro takılmaları veya gecikmeleri fark etmese bile, uygulamanın genel performansı ve enerji tüketimi olumsuz etkilenir.
React’in batching mekanizması, bu israfı önlemek için tasarlanmıştır. Birden fazla state güncellemesini tek bir yeniden render döngüsünde birleştirerek, gereksiz DOM manipülasyonlarını minimuma indirir. Bu, kullanıcı arayüzünün daha akıcı olmasını, daha az CPU kullanmasını ve genel olarak daha hızlı tepki vermesini sağlar. React 18 ile birlikte gelen “Otomatik Batching” ise bu optimizasyonu daha da ileriye taşıyarak, React geliştiricilerinin hayatını önemli ölçüde kolaylaştırmıştır.
React 18 Öncesi ve Sonrası Batching Mekanizmaları: Farklar Neler?
React’in batching mekanizması her zaman var olmuştur, ancak zamanla evrim geçirerek daha kapsamlı ve otomatik hale gelmiştir. Özellikle React 18, bu konuda devrim niteliğinde bir adım atarak, geliştiricilerin performansı artırmak için daha az manuel çaba sarf etmesini sağlamıştır.
React 18 Öncesi: Senkron ve Asenkron Senaryolar
React 18’den önce batching, genellikle tarayıcı olayları (event handlers) içinde yapılan state güncellemeleriyle sınırlıydı. Yani, bir butona tıklama veya bir form gönderme gibi doğrudan React tarafından yönetilen olaylar içinde birden fazla setState çağrısı yapıldığında, React bunları otomatik olarak gruplandırır ve tek bir yeniden render işlemiyle hallederdi. Örneğin:
function Sayac() {
const [sayi, setSayi] = React.useState(0);
const [aktif, setAktif] = React.useState(false);
const handleClick = () => {
setSayi(s => s + 1); // 1. state güncellemesi
setAktif(a => !a); // 2. state güncellemesi
// React 18 öncesi ve sonrası: Bu iki güncelleme tek bir renderda birleşir.
};
return (
Sayı: {sayi}
Durum: {aktif ? 'Aktif' : 'Pasif'}
);
}
Yukarıdaki örnekte, handleClick fonksiyonu içinde setSayi ve setAktif çağrıları tek bir kullanıcı olayında (buton tıklaması) gerçekleştiği için, React 18 öncesinde de bu iki güncelleme tek bir yeniden renderda birleştirilirdi. Yani, kullanıcı butona tıkladığında sadece bir kez yeniden render işlemi gerçekleşirdi.
Ancak, durum olay işleyicilerinin dışına çıktığında farklıydı. Özellikle zamanuyumsuz (asenkron) işlemler, setTimeout çağrıları, promise'ler (.then() blokları) veya veri çekme (data fetching) işlemleri sonucunda yapılan state güncellemeleri, React 18 öncesinde otomatik olarak gruplandırılmazdı. Her bir asenkron setState çağrısı kendi başına bir yeniden render tetiklerdi. Örneğin:
function VeriCekme() {
const [veri, setVeri] = React.useState(null);
const [yukleniyor, setYukleniyor] = React.useState(false);
const fetchData = async () => {
setYukleniyor(true); // 1. render (React 18 öncesinde ayrı render)
const response = await fetch('/api/data');
const data = await response.json();
setVeri(data); // 2. render (React 18 öncesinde ayrı render)
setYukleniyor(false); // 3. render (React 18 öncesinde ayrı render)
};
return (
{yukleniyor && Yükleniyor...
}
{veri && Veri: {JSON.stringify(veri)}
}
);
}
Bu örnekte, React 18 öncesinde fetchData fonksiyonu çağrıldığında toplam üç yeniden render işlemi gerçekleşirdi. setYukleniyor(true), setVeri(data) ve setYukleniyor(false) çağrılarının her biri, asenkron bir bağlamda oldukları için ayrı ayrı bir render döngüsünü tetiklerdi. Bu durum, özellikle çok sayıda asenkron güncelleme yapılan uygulamalarda performansı olumsuz etkileyen bir faktördü.
React 18 ve Otomatik Batching'in Yükselişi
React 18 ile birlikte, "Otomatik Batching" (Automatic Batching) kavramı hayatımıza girdi ve önceki sınırlamaları ortadan kaldırdı. React 18'den itibaren, tüm state güncellemeleri, nerede yapıldıklarından bağımsız olarak (olay işleyicileri, promise'ler, setTimeout, asenkron fonksiyonlar vb.), mümkün olduğunca otomatik olarak gruplandırılır. Bu, yukarıdaki VeriCekme senaryosunun artık çok daha verimli çalışacağı anlamına geliyor.
function VeriCekmeReact18() {
const [veri, setVeri] = React.useState(null);
const [yukleniyor, setYukleniyor] = React.useState(false);
const fetchData = async () => {
setYukleniyor(true); // React 18: Bu güncellemeler tek bir renderda birleşir
const response = await fetch('/api/data');
const data = await response.json();
setVeri(data); // React 18: Bu güncellemeler tek bir renderda birleşir
setYukleniyor(false); // React 18: Bu güncellemeler tek bir renderda birleşir
// Tüm bu üç setState çağrısı, React 18'de sadece 1 kez yeniden render tetikler!
};
return (
{yukleniyor && Yükleniyor...
}
{veri && Veri: {JSON.stringify(veri)}
}
);
}
React 18 ile birlikte, fetchData fonksiyonu içindeki üç setState çağrısı da tek bir yeniden render döngüsünde gruplandırılır. Bu, performans açısından muazzam bir iyileşme sağlar çünkü gereksiz iki yeniden render işlemi ortadan kalkar. Bu sayede, uygulamanız daha hızlı tepki verir ve daha verimli çalışır. Otomatik batching, React 18'in temel konseptlerinden biri olan "concurrency" (eşzamanlılık) ile el ele çalışarak, React'in dahili scheduler'ının güncellemeleri daha akıllıca önceliklendirmesine ve işlemesine olanak tanır. Bu, geliştiricilerin performans endişeleriyle daha az uğraşarak iş mantığına odaklanmasına olanak tanır.
Özetle, React 18 öncesi batching yalnızca React'in kendi olay işleyicileri içindeki senkron güncellemelerle sınırlıyken, React 18 ile birlikte otomatik batching, tüm state güncellemelerini kapsayacak şekilde genişlemiş ve uygulamanın genel performansını doğal olarak iyileştirmiştir.
Kod Üzerinde Otomatik Batching'i Anlamak: Adım Adım Örnekler
Teorik bilgileri pekiştirmek ve otomatik batching'in gücünü daha iyi kavramak için şimdi birkaç kod örneğine göz atalım. Bu örnekler, farklı senaryolarda React'in kaç kez yeniden render işlemi yaptığını somut olarak gösterecek.
Basit Bir State Güncellemesi Senaryosu
Bir buton tıklaması ile birden fazla state'i güncellediğimiz en temel senaryo ile başlayalım. React 18'de beklediğimiz gibi, bu güncellemelerin tek bir yeniden renderda birleşmesi gerekir.
function BasitOrnek() {
const [sayac, setSayac] = React.useState(0);
const [mesaj, setMesaj] = React.useState('');
console.log('BasitOrnek render edildi!');
const handleUpdate = () => {
setSayac(prev => prev + 1);
setMesaj(Sayac ${sayac + 1} oldu);
// Bu iki setState çağrısı, tek bir event handler içinde olduğu için
// React 18'de otomatik olarak birleştirilecek ve sadece 1 render tetikleyecektir.
};
return (
Basit State Güncellemesi
Sayaç: {sayac}
Mesaj: {mesaj}
);
}
Bu kodu çalıştırıp butona tıkladığınızda, konsolda sadece bir kez "BasitOrnek render edildi!" mesajını göreceksiniz. Bu, React'in her iki setState çağrısını da yakalayıp tek bir yeniden renderda birleştirdiğini kanıtlar. Bu, özellikle etkileşimli bileşenlerde performansı artıran çok değerli bir özelliktir.
Zamanlayıcılar ve Asenkron İşlemlerle Batching Davranışı
React 18'in en büyük yeniliklerinden biri, asenkron bağlamlardaki state güncellemelerini de otomatik olarak gruplandırmasıydı. Şimdi bu senaryoyu test edelim.
function AsenkronOrnek() {
const [sayi, setSayi] = React.useState(0);
const [yukleniyor, setYukleniyor] = React.useState(false);
console.log('AsenkronOrnek render edildi!');
const fetchDataSimulated = () => {
setYukleniyor(true); // Bu ilk setState
setTimeout(() => {
setSayi(100); // Bu ikinci setState
setYukleniyor(false); // Bu üçüncü setState
// React 18'de bu üç setState çağrısı da otomatik olarak birleşecek
// ve setTimeout içindeki güncellemeler ile setYukleniyor(true) çağrısı
// bir bütün olarak tek bir renderda işlenecektir.
}, 1000);
};
return (
Asenkron State Güncellemesi (setTimeout)
Sayı: {sayi}
Durum: {yukleniyor ? 'Yükleniyor...' : 'Hazır'}
);
}
Bu örneği çalıştırdığınızda ve butona tıkladığınızda neler gözlemlersiniz? Öncelikle, butona tıklandığında hemen "AsenkronOrnek render edildi!" mesajını göreceksiniz (setYukleniyor(true) nedeniyle). Ardından, 1 saniye sonra setTimeout içindeki diğer iki setState çağrısı gerçekleştiğinde, konsolda sadece bir kez daha "AsenkronOrnek render edildi!" mesajını göreceksiniz. Bu, React 18'in asenkron çağrıları da toplu olarak işlediğinin açık bir göstergesidir.
setTimeout ile aynı mantıkta çalışır. await keyword'ünden önce ve sonra yapılan tüm state güncellemeleri, mümkünse tek bir renderda birleştirilir. Bu, özellikle veri çekme döngülerinde uygulamalarınızın çok daha performanslı olmasını sağlar.ReactDOM.flushSync ile Batching'i Manuel Yetersiz Kılmak
Peki ya bazen, gerçekten anında bir güncellemeye ihtiyacımız olursa? Otomatik batching genellikle harika olsa da, bazı niş senaryolarda bir state güncellemesinin hemen DOM'a yansımasını isteyebiliriz. Örneğin, bir ölçüm yapmadan önce DOM'un belirli bir state'e güncellenmesi gerekebilir. İşte bu noktada ReactDOM.flushSync devreye girer.
flushSync, içine aldığı fonksiyon içindeki tüm state güncellemelerinin anında (senkron olarak) işlenmesini ve DOM'a yansımasını zorlar. Bu, batching mekanizmasını geçici olarak devre dışı bırakır.
import ReactDOM from 'react-dom';
function FlushSyncOrnek() {
const [mesaj1, setMesaj1] = React.useState('');
const [mesaj2, setMesaj2] = React.useState('');
console.log('FlushSyncOrnek render edildi!');
const handleUpdate = () => {
// İlk güncellemeyi hemen DOM'a yansıtmak istiyoruz
ReactDOM.flushSync(() => {
setMesaj1('İlk mesaj hemen görünecek');
// Bu çağrıdan sonra bileşen hemen render edilir.
console.log('FlushSync bloğu içinde render sonrası:', document.getElementById('mesaj1-div')?.textContent);
});
console.log('FlushSync bloğundan sonra render edildi (1. render bitti)');
// İkinci güncelleme normal batching kurallarına göre işlenir (eğer tek başına ise)
setMesaj2('İkinci mesaj normal batching ile görünecek');
// Bu çağrı, eğer başka bir batched güncelleme yoksa ayrı bir render tetikler.
};
return (
ReactDOM.flushSync Kullanımı
Mesaj 1: {mesaj1}
Mesaj 2: {mesaj2}
);
}
Bu örnekte, butona tıkladığınızda:
ReactDOM.flushSynciçindekisetMesaj1çağrılır. "FlushSyncOrnek render edildi!" mesajı konsola yazılır. İlk mesaj hemen güncellenir ve DOM'a yansır.flushSyncbloğundan çıkıldıktan sonra,setMesaj2çağrılır. Bu çağrı, tek başına olduğu için (veya başka batched bir güncelleme ile birlikte) ikinci kez "FlushSyncOrnek render edildi!" mesajını konsola yazdırır ve ikinci mesaj güncellenir.
Yani, flushSync kullanıldığında, tek bir olay işleyicisi içinde olmamıza rağmen iki ayrı yeniden render işlemi gerçekleşir. Bu, flushSync'in batching'i nasıl böldüğünü açıkça gösterir. flushSync'i dikkatli kullanmak önemlidir, çünkü performansı olumsuz etkileyebilir ve React'in dahili scheduler'ının işini zorlaştırabilir. Genellikle, kullanıcı arayüzünü ölçmek veya belirli animasyonları tetiklemek gibi özel, senkronize edilmiş DOM etkileşimlerinin gerektiği durumlar için ayrılmıştır.
Gerçek Dünya Senaryolarında Otomatik Batching Optimizasyonu
Otomatik batching'in sadece küçük örneklerde değil, gerçek dünya uygulamalarında da ne kadar etkili olduğunu görmek, onun değerini daha iyi anlamamızı sağlar. İşte bazı yaygın senaryolar ve otomatik batching'in bu durumlarda nasıl performans iyileştirmeleri sağladığı:
Form Yönetimi ve Çoklu State Güncellemeleri
Birçoğumuzun React uygulamalarında en sık karşılaştığı senaryolardan biri karmaşık formlardır. Kullanıcı bir formu gönderdiğinde, genellikle birden fazla state'in güncellenmesi gerekir: form gönderiliyor durumunu ayarlamak (setIsSubmitting(true)), hata mesajlarını temizlemek (setErrors({})), başarı mesajı göstermek (setSuccessMessage('Başarılı!')) ve belki de bazı form alanlarını sıfırlamak (setFormData({})). React 18 öncesinde, bu güncellemeler genellikle ayrı ayrı render'lara neden olurdu, çünkü çoğu zaman form gönderme işlemi bir API çağrısı gibi asenkron bir sürecin sonucunda gerçekleşirdi.
function KayitFormu() {
const [ad, setAd] = React.useState('');
const [email, setEmail] = React.useState('');
const [hata, setHata] = React.useState(null);
const [gonderiliyor, setGonderiliyor] = React.useState(false);
const [basarili, setBasarili] = React.useState(false);
console.log('KayitFormu render edildi!');
const handleSubmit = async (e) => {
e.preventDefault();
setGonderiliyor(true); // 1. setState
setHata(null); // 2. setState
setBasarili(false); // 3. setState
try {
// API çağrısı simülasyonu
await new Promise(resolve => setTimeout(resolve, 1500));
if (ad === '' || email === '') {
throw new Error('Tüm alanları doldurunuz.');
}
console.log('Form verileri:', { ad, email });
setBasarili(true); // 4. setState
} catch (err) {
setHata(err.message); // 5. setState
} finally {
setGonderiliyor(false); // 6. setState
// Tüm bu setState çağrıları (setGonderiliyor(true) hariç, o anında render'ı tetiklerdi
// React 18 öncesinde), Otomatik Batching sayesinde tek bir renderda birleşir.
}
};
return (
Kayıt Formu (Otomatik Batching ile)
{hata && Hata: {hata}
}
{basarili && Kaydınız başarıyla oluşturuldu!
}
);
}
React 18'de, handleSubmit içindeki ilk üç setState çağrısı (setGonderiliyor(true), setHata(null), setBasarili(false)) tek bir renderda birleşir. Ardından, await işleminden sonra try/catch/finally bloklarındaki tüm diğer setState çağrıları da kendi aralarında tek bir renderda birleşir. Böylece, kullanıcı butona tıkladığında iki kez "KayitFormu render edildi!" mesajını görürüz: biri formun gönderilme başlangıcında, diğeri ise veri çekme işlemi tamamlandığında. React 18 öncesinde ise bu sayı çok daha fazla olabilirdi. Bu, özellikle veri doğrulama ve kullanıcı geri bildirimleri içeren dinamik formlarda ciddi bir performans artışı sağlar.
Veri Çekme (Data Fetching) İşlemlerinde Performans İyileştirmeleri
Modern React uygulamalarının bel kemiği olan veri çekme işlemleri, sıkça asenkron state güncellemelerini içerir. Bir API'den veri çekerken genellikle yüklenme durumunu (loading state), hata durumunu (error state) ve nihayetinde verinin kendisini (data state) yönetmemiz gerekir. Otomatik batching sayesinde, bu üç state güncellemesi de tek bir yeniden render döngüsünde birleştirilerek gereksiz render'ların önüne geçilir.
function UrunListesi() {
const [urunler, setUrunler] = React.useState([]);
const [yukleniyor, setYukleniyor] = React.useState(true);
const [hata, setHata] = React.useState(null);
console.log('UrunListesi render edildi!');
React.useEffect(() => {
const fetchUrunler = async () => {
setYukleniyor(true); // 1. setState
setHata(null); // 2. setState
setUrunler([]); // 3. setState
try {
const response = await fetch('https://api.example.com/products');
if (!response.ok) {
throw new Error('Ürünler çekilemedi');
}
const data = await response.json();
setUrunler(data); // 4. setState
} catch (err) {
setHata(err.message); // 5. setState
} finally {
setYukleniyor(false); // 6. setState
// React 18'de tüm bu setState çağrıları otomatik olarak gruplandırılır.
// useEffect'in ilk çalışmasında ve veri çekme tamamlandığında toplam 2 render gerçekleşir.
}
};
fetchUrunler();
}, []); // Bileşen mount edildiğinde sadece bir kez çalışır
if (yukleniyor) return Ürünler yükleniyor...
;
if (hata) return Hata: {hata}
;
return (
Ürün Listesi
{urunler.map(urun => (
- {urun.ad} - {urun.fiyat} TL
))}
);
}
UrunListesi bileşeni mount edildiğinde, useEffect içindeki fetchUrunler fonksiyonu çalışır. İlk üç setState çağrısı (setYukleniyor(true), setHata(null), setUrunler([])) otomatik olarak gruplanır ve tek bir render tetikler. Veri çekme işlemi tamamlandıktan sonra, setUrunler(data) veya setHata(err.message) ve setYukleniyor(false) çağrıları da yine otomatik olarak gruplanır ve ikinci bir render tetikler. Yani, bu senaryoda toplamda sadece iki kez "UrunListesi render edildi!" mesajını görürüz. React 18 öncesinde ise bu sayı beş veya altı olabilirdi, bu da önemli bir fark yaratır.
Kritik Etkileşimler İçin Akıllı Batching Stratejileri
Çoğu durumda otomatik batching yeterli olsa da, bazen kullanıcı arayüzünde belirli bir sırayla veya anında görünmesi gereken kritik güncellemeler olabilir. Örneğin, bir sürükle-bırak işlemi sırasında elementin konumunu anında güncellemek veya bir animasyonun başlangıç noktasını kesin olarak belirlemek gerekebilir. Bu tür niş durumlarda ReactDOM.flushSync'i kullanmak, batching'i geçici olarak devre dışı bırakıp belirli bir state güncellemesini hemen DOM'a yansıtmaya zorlayabiliriz.
import ReactDOM from 'react-dom';
function AnimasyonluOge() {
const [xPos, setXPos] = React.useState(0);
const [yPos, setYPos] = React.useState(0);
const ref = React.useRef(null);
console.log('AnimasyonluOge render edildi!');
const handleMouseMove = (e) => {
// Fare hareketinde elementin konumunu hemen güncellemek isteyebiliriz.
// Batching, her fare hareketinde 1 render yerine,
// bu durumu geciktirebilir ve bu da animasyonun akıcılığını bozabilir.
// Bu yüzden flushSync kullanmak mantıklı olabilir.
ReactDOM.flushSync(() => {
setXPos(e.clientX);
setYPos(e.clientY);
});
// Diğer iş mantığı burada devam edebilir. Örneğin, bir gecikmeli işlem:
// setTimeout(() => { /* başka bir state güncellemesi */ }, 50);
};
React.useEffect(() => {
window.addEventListener('mousemove', handleMouseMove);
return () => window.removeEventListener('mousemove', handleMouseMove);
}, []);
return (
Animasyonlu Öğe (Konum: {xPos}, {yPos})
Fareyi ekran üzerinde hareket ettirin.
);
}
Bu örnekte, handleMouseMove fonksiyonu her fare hareketinde tetiklenir. Normalde React, bu kadar sık tetiklenen state güncellemelerini optimize etmeye çalışır. Ancak bir animasyonun veya sürükle-bırak işleminin akıcı olması için her hareketin anında DOM'a yansıması gerekebilir. ReactDOM.flushSync kullanarak setXPos ve setYPos güncellemelerinin hemen işlenmesini sağlarız. Bu, konsolda her fare hareketiyle "AnimasyonluOge render edildi!" mesajını görmemize neden olacaktır, bu da beklenen bir davranıştır, çünkü bu senaryoda anlık DOM güncellemesi önceliklidir.
flushSync kullanımı seyrek olmalı ve yalnızca gerçekten ihtiyaç duyulan durumlarda tercih edilmelidir. Aşırı kullanımı, uygulamanın genel performansını ve kullanıcı deneyimini olumsuz etkileyebilir. Çoğu durumda, otomatik batching sizin için en iyi performansı sağlayacaktır.Bu gerçek dünya senaryoları, otomatik batching'in React uygulamalarımızın performansını nasıl doğal yollarla artırdığını ve biz geliştiricilerin daha az kodla daha verimli uygulamalar geliştirmemize olanak tanıdığını açıkça göstermektedir. flushSync gibi araçlar ise nadir ve özel durumlarda bize manuel kontrol sağlama imkanı sunar.
Otomatik Batching ile İlgili Sıkça Sorulan Sorular
React'in otomatik batching mekanizması, performans optimizasyonunda güçlü bir araç olsa da, bazen geliştiricilerin kafasında soru işaretleri bırakabilir. İşte bu konuda en sık karşılaşılan sorular ve cevapları:
Otomatik Batching Her Durumda Çalışır mı?
React 18'den itibaren otomatik batching, çoğu durumda çalışır. Bu, olay işleyicileri (event handlers), setTimeout çağrıları, promise'ler ve asenkron fonksiyonlar içinde yapılan state güncellemelerini kapsar. Ancak, React'in kontrolünün dışında gerçekleşen bazı durumlar vardır. Örneğin, tarayıcının kendi addEventListener API'si ile eklenen bir olay işleyicisi içinde doğrudan setState çağırırsanız (yani React'in sentetik olay sistemi üzerinden değil), React bu güncellemeyi otomatik olarak batchleyemeyebilir. Çünkü React'in scheduler'ı bu olayın farkında değildir. Yine de, çoğu modern React uygulaması React'in sentetik olay sistemini kullandığı için bu durumla nadiren karşılaşırsınız.
Performans İçin Başka Neler Yapabilirim?
Otomatik batching harika bir başlangıç noktası olsa da, React performansını artırmak için yapabileceğiniz başka şeyler de var:
React.memo,useCallback,useMemo: Gereksiz yeniden render'ları önlemek için bileşenleri, fonksiyonları ve değerleri hafızaya alın (memoize edin).- Anahtar (Key) Kullanımı: Listelerde benzersiz ve sabit anahtarlar kullanmak, React'in elemanları verimli bir şekilde tanımasına ve güncellemesine yardımcı olur.
- State'i Yeterli Düzeyde Tutmak: Gereksiz yere global state kullanmak yerine, state'i gerçekten ihtiyacı olan bileşene yakın tutun.
- Virtualization (Sanallaştırma): Çok uzun listeler için sadece görünürdeki elemanları render etmek, performansı önemli ölçüde artırır (örneğin,
react-windowveyareact-virtualizedkütüphaneleri). - React Developer Tools: Performans sorunlarını tespit etmek için tarayıcı eklentilerini (profiler sekmesi) etkin bir şekilde kullanın.
flushSync Kullanımı Ne Zaman Gereklidir?
ReactDOM.flushSync, normalde otomatik batching tarafından birleştirilecek olan state güncellemelerinin anında ve senkron olarak DOM'a yansımasını sağlamak için kullanılır. Bu, nadir durumlarda gerekli olabilir:
- DOM Ölçümleri: Bir state güncellemesinden hemen sonra DOM'un yeni halini ölçmeniz gerektiğinde (örneğin, bir elementin boyutunu veya konumunu).
- Kritik Animasyonlar/Geçişler: Bir animasyonun veya geçişin başlangıç durumunun anında görünmesi gerektiğinde, özellikle tarayıcının sonraki çizim döngüsünden önce.
- Kullanıcı Etkileşimlerinin Anında Geri Bildirimi: Sürükle-bırak gibi anlık geri bildirim gerektiren karmaşık etkileşimlerde, kullanıcı arayüzünün takılmaması için.
flushSync'i kullanmadan önce, sorununuzu çözmenin daha "React dostu" bir yolu olup olmadığını iki kez düşünün. Genellikle, React'in kendi scheduler'ı çoğu senaryoda daha iyi performans sağlar.Sonuç
React'in otomatik batching özelliği, modern uygulamalarımızın performansını doğal ve zahmetsiz bir şekilde artıran temel bir optimizasyon mekanizmasıdır. React 18 ile birlikte gelen geliştirmeler sayesinde, geliştiriciler artık asenkron bağlamlardaki state güncellemelerinin de otomatik olarak birleştirilmesinden faydalanabiliyor. Bu, "kaç yeniden render?" sorusunun cevabını çoğu zaman "çok daha az" olarak değiştirmekte ve uygulamanızın daha akıcı, daha hızlı ve daha tepkisel olmasını sağlamaktadır.
Otomatik batching'i anlamak ve onun avantajlarından yararlanmak, React uygulamalarınızın potansiyelini tam olarak kullanmanız için kritik öneme sahiptir. Nadir durumlarda ReactDOM.flushSync gibi araçlara ihtiyaç duyulsa da, genel olarak React'in bu dahili optimizasyonuna güvenmek, hem geliştirme sürecinizi kolaylaştıracak hem de son kullanıcıya daha iyi bir deneyim sunacaktır. Performans optimizasyonu yolculuğunuzda, otomatik batching'in sizin en iyi dostlarınızdan biri olacağını unutmayın.
