React 18 ve concurrent render
React 18'in eşzamanlı render modeli uygulamanın hangi davranışlarını değiştiriyor ve bu değişimi pratikte nasıl karşılamak gerekiyor.
Bu yazı 4 yıl önce yazıldı — o günün koşullarını yansıtır.
React 18, Mart 2022’de yayımlandı. Duyuru notları okunduğunda en sık geçen terim “concurrent rendering”. Ama bu terim birçok şeyi çağrıştırabiliyor — multithreading mi, asenkron operasyonlar mı, başka bir şey mi?
Bu yazıda React 18’in render modelindeki değişimi, bunun uygulama davranışına yansımalarını ve pratikte ne yapılması gerektiğini anlatmaya çalışacağım.
Concurrent rendering ne anlama geliyor
React’in önceki sürümlerinde render işlemi synchronous ve kesintisizdi. React bir bileşen ağacını render etmeye başladığında tamamlanana kadar durmazdı. Bu, büyük güncellemelerin arayüzü donuklaştırma riskini taşıdığı anlamına geliyordu: kullanıcı bir şeye tıklıyor ama yanıt milisaniyeler yerine yüzlerce milisaniye sonra geliyor.
Concurrent rendering, render sürecini bölünebilir (interruptible) hale getiriyor. React, render işini parçalara bölüp tarayıcıya nefes alma fırsatı verebiliyor. Daha acil bir güncelleme geldiğinde (kullanıcı bir şeye tıkladı, klavyeden yazdı) React mevcut render’ı askıya alıp acil işi önce yapabiliyor.
Önemli bir not: bu asenkron JavaScript değil. JavaScript hâlâ tek iş parçacıklı (single-threaded). React kendi scheduler’ını kullanarak işin önceliğini düzenliyor; tarayıcıya aradaki boşluklarda çalışma fırsatı veriyor.
createRoot ile yeni render modu
React 18’deki değişiklikleri kullanmak için yeni root API’sine geçmek gerekiyor:
// Eski yol (React 17)
import ReactDOM from 'react-dom';
ReactDOM.render(<App />, document.getElementById('root'));
// Yeni yol (React 18)
import { createRoot } from 'react-dom/client';
const root = createRoot(document.getElementById('root'));
root.render(<App />);
Bu tek satır değişikliği, uygulamanın concurrent özelliklere erişim kazanması için yeterli. Var olan kod büyük ölçüde çalışmaya devam ediyor.
useTransition: acil olmayan güncellemeleri işaretlemek
Concurrent rendering’in en doğrudan API’si useTransition hook’u. Bir güncellemenin “acil değil” olarak işaretlenmesini sağlıyor:
import { useState, useTransition } from 'react';
function SearchResults() {
const [query, setQuery] = useState('');
const [results, setResults] = useState([]);
const [isPending, startTransition] = useTransition();
function handleChange(e) {
// Acil: input değerini hemen güncelle
setQuery(e.target.value);
// Acil değil: sonuçları gecikmeli güncelle
startTransition(() => {
setResults(filterResults(e.target.value));
});
}
return (
<>
<input value={query} onChange={handleChange} />
{isPending ? <span>Aranıyor...</span> : null}
<ResultList results={results} />
</>
);
}
startTransition içindeki güncelleme, daha acil güncellemeler geldiğinde ertelenebilir. Kullanıcı hızla yazdığında süren ağır bir liste render’ı sonraki tuş vuruşunu engellemez; React o render’ı yarıda keser, önce girdiyi işler, ardından liste render’ını yeniden başlatır.
Otomatik batching: sessiz ama önemli değişim
React 18 ile birlikte automatic batching kapsamı genişledi. React 17’de event handler’lar içindeki birden fazla setState zaten birleştirilerek tek render üretirdi. Ama asenkron kodda (setTimeout, Promise callback) her setState ayrı render tetiklerdi.
React 18’de bu ayrım kalktı: her koşulda setState çağrıları otomatik olarak birleştiriliyor.
// React 18'de bu yalnızca tek render üretiyor
setTimeout(() => {
setCount(c => c + 1);
setName('yeni isim');
}, 1000);
Bu değişiklik çoğu uygulama için sessiz bir performans iyileştirmesi. Ama bazı kodlarda beklenmedik davranış değişikliğine yol açabilir; dikkatli olmak gerekiyor.
Geçişin pratikte maliyeti
React 18’e geçiş büyük projelerde yapılacak değişiklikler göz önüne alındığında hafif. createRoot değişikliği zorunlu; useTransition veya Suspense için yeni yazımlar opsiyonel, ihtiyaç hissedildikçe ekleniyor.
Dikkat edilmesi gereken: useEffect’in davranışı Strict Mode’da değişti. React 18 Strict Mode, geliştirme modunda her efekt için kasıtlı olarak fazladan bir kurulum ve temizlik döngüsü çalıştırıyor — efektlerin mount/unmount döngüsüne dayanıklı olup olmadığını test etmek için. Bazı yan etkilerin bu durumda beklenmedik biçimde davranabileceğini test ortamında görmek mümkün.
React 18, render modelini köklü biçimde değiştiriyor; ama bu değişikliği kademeli ve opt-in olarak sunuyor. Bu yaklaşım çok sayıda bileşeni olan büyük uygulamalar için doğru strateji. Her şeyi bir seferde değiştirmeye zorlamadan, hazır olanı alın, gerisini ilerleyin.
Bu konudaki deneyler
480 altıgende 60 turda biten, tarayıcıda çevrimdışı çalışan zarsız strateji oyunu çekirdeği; Regnarium olarak yayına çıktı.
Şu an ne yapıyor
480 altıgenlik haritada azami 60 turda biten bir 4X eli yürütüyor; kurallar, harita üretimi ve rakip yapay zekâ tek bir saf TypeScript çekirdeğinde, savaş zarsız. Elinde yarım saati olan ve strateji oynamak isteyen herkes tarayıcıdan açabilir.
Düğüm-çizgi bir tahtada deterministik hamlelerle ilerleyen, çevrimdışı bir bulmaca oyunu; 36+6 bölümü tek bir TypeScript çekirdeği web ve mobile besliyor.
Şu an ne yapıyor
Düğüm-çizgi bir tahtada 36 bölüm + 6 gizli bölümü, şans ya da refleks olmadan tamamen deterministik hamlelerle çözdürüyor; tek TypeScript çekirdeği web (PWA) ve mobil (iOS/Android) istemcisini aynı anda besliyor. silkward.com üzerinden tarayıcıdan oynanabilir.
Yorumlar
Yorum yapmak için GitHub hesabınızla giriş yapmanız yeterli. Yorumlar GitHub Discussions üzerinde saklanır.