React Render zamanlamasını akıllıca yönetmenin yolu
React, kullanıcı etkileşimlerine verdiği tepki süresini kısaltmak için yeni bir dönem başlattı.
React 18 ile gelen Concurrent Rendering sistemi sayesinde artık render işlemleri önceliklendirilerek çalışıyor.
Ve bu dünyada iki yeni kahraman var: useTransition ve useDeferredValue.
Eğer Vue.js'e aşinaysanız nextTick, Suspense, flush: 'post', useDebounce, useThrottle, watchEffect ile custom çözümlerin default olarak zaten var olduğunu bilirsiniz ve aslında mantık aynı.
Bu yazıda, bu iki hook’un nasıl çalıştığını, farklarını ve pratik kullanım senaryolarını inceleyeceğiz.
Ön Bilgi: Concurrent Rendering Nedir?
React 18’den önce her render işlemi senkrondu. Yani kullanıcı yazarken bile React her değişiklikte UI’ı baştan oluşturuyordu. Şimdi React render işlemlerini öncelik sırasına koyabiliyor:- Acil (urgent) işler → buton tıklama, input yazma.
- Geç (non-urgent) işler → filtreleme, liste güncelleme, grafik çizimi.
useTransition: Yoğun Render İşlerini Ertele
useTransition, uzun süren render işlemlerini “arka plana” alır. Yani React’a der ki: “Önce kullanıcıyı bekletme, bu işi sonra yap.”const [isPending, startTransition] = useTransition()
function handleChange(e) {
const value = e.target.value
startTransition(() => {
setFilter(value)
})
}
Ne Oluyor Burada?
- Kullanıcı inputa yazıyor.
- React önce input değerini hemen güncelliyor (acil iş).
- Liste filtreleme işlemi
startTransitioniçine alındığı için daha sonra yapılır (non-urgent iş).
isPending Ne İşe Yarar?
isPending true olduğunda, React geç işlerin devam ettiğini belirtir.{isPending && <span>Filtreleniyor...</span>}
Bu sayede kullanıcıya yüklenme durumunu gösterebilirsin.Özet: useTransition = "Render’ı geciktir, ama kullanıcı deneyimini koru."
Gerçek Hayat Örneği
function Search() {
const [query, setQuery] = useState('')
const [results, setResults] = useState([])
const [isPending, startTransition] = useTransition()
const handleChange = (e) => {
const value = e.target.value
setQuery(value)
startTransition(() => {
setResults(bigData.filter(item => item.includes(value)))
})
}
return (
<>
<input value={query} onChange={handleChange} placeholder="Ara..." />
{isPending && <p>Aranıyor...</p>}
<ul>
{results.map(r => <li key={r}>{r}</li>)}
</ul>
</>
)
}
Input anında güncellenir, ama filtreleme işi arkaya atılır. Sonuç: Donmayan, pürüzsüz bir arama deneyimi. ✅useDeferredValue: Değeri Erteleyerek Güncelle
useDeferredValue, bir değeri “gecikmeli” hale getirir. Bu sayede o değere bağlı render işlemleri hemen değil, sistem uygun olduğunda çalışır.const deferredQuery = useDeferredValue(query)
const filteredList = bigData.filter(item => item.includes(deferredQuery))
Burada query anında değişir, ama deferredQuery biraz sonra güncellenir. Yani kullanıcı inputu yazarken UI donar gibi olmaz.useTransition vs useDeferredValue
| Özellik | useTransition | useDeferredValue |
|---|---|---|
| Ne yapar? | Bir state güncellemesini erteler | Bir değerin güncellenmesini erteler |
| Kullanım yeri | State setter etrafında | Değerin kendisinde |
| Kontrol kimde? | Geliştiricide (startTransition) | React’te |
| Kullanım amacı | Yoğun state değişimleri | Ağır hesaplama gerektiren değerler |
Birlikte Kullanım Örneği
function FilterList({ items }) {
const [query, setQuery] = useState('')
const deferredQuery = useDeferredValue(query)
const [isPending, startTransition] = useTransition()
const filtered = items.filter(i => i.includes(deferredQuery))
return (
<>
<input
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Filtrele..."
/>
{isPending && <p>Güncelleniyor...</p>}
<ul>
{filtered.map(f => <li key={f}>{f}</li>)}
</ul>
</>
)
}
Burada:queryanında değişiyor.deferredQuerybiraz gecikmeli geliyor.- UI hiçbir zaman takılmıyor.
Neden Önemli?
React, her render’ı eşit öncelikte çalıştırırsa, kullanıcı deneyimi kötüleşir. Büyük bir proje düşün; tüm kodlar aynı anda çalışmaya çalışırsa sistem çökebilir veya çok yavaşlayabilir. React ise bu iki hook sayesinde artık şunu ayırt ediyor:- “Kullanıcının gördüğü şeyler acil.”
- “Listeyi sonra da güncelleyebilirim.”
Performans Tavsiyeleri
- Uzun süren hesaplamaları
startTransitioniçine al. - Kullanıcı etkileşimlerini (click, input) asla bloklama.
- Geri bildirim vermek için
isPendingdurumunu göster. - Gereksiz render’ları azaltmak için
useMemoveuseCallbackkullan.
useTransition + Suspense = Güçlü İkili
React 18 ileSuspense artık data fetching süreçlerinde useTransition ile birlikte çalışabiliyor.startTransition(() => {
setResource(fetchData())
})
React, bu geçişi “low priority” olarak işaretliyor ve kullanıcıyı bekletmeden loading durumunu gösteriyor.💡 Pro tip: Transition’lar artık “UI state machine” mantığıyla davranıyor -- bekleme, gösterme, güncelleme sırası otomatikleşti.
Gözle Görülmeyen Kazanç
Bir input’un 200ms geç tepki verdiğini fark etmeyebilirsin, ama kullanıcı fark eder.useTransition ve useDeferredValue, bu farkı görünmez hale getirir. Küçük hissedilen farklar, büyük deneyim kazancı yaratır.Sonuç
React 18 ve sonrası, sadece "render eden" değil, önceliklendiren bir framework. Bu iki hook, modern arayüzlerde akıcılığı garanti altına alır.Kısaca:
useTransition→ Ağır state değişimlerini ertele.useDeferredValue→ Değeri geciktirerek performansı koru.- İkisini birlikte kullan → Donmayan, nefes alan bir UI yarat.