Merhaba arkadaşlar, bugün React'te yeni başlayanların (hatta bazen tecrübelilerin) kafasını karıştıran bir konudan bahsedeceğim. useState'in functional update formu! Yani şu setCount(prevCount => prevCount + 1) yazdığımız sihirli yöntem. Bu yazıyı yazma sebebim, geçenlerde bir bileşenimde state güncellemelerimin bir kısmının kaybolduğunu fark etmem oldu. Debug ederken kafayı yemiştim, ta ki React'in batch update (toplu güncelleme)[/COLOR] mekanizması ve bu formun önemiyle yüzleşene kadar.
Neden Sadece "setState(5)" Yetmez Ki?
Temel kullanımda, state'i doğrudan yeni bir değerle güncelleriz: setIsOpen(true). Bu mükemmel çalışır. Sorun, mevcut state değerine bağlı olan güncellemelerde başlar. Özellikle de bu güncellemeler aynı render cycle içinde, asenkron bir işlemin (event handler, timeout, fetch) içinde art arda yapılıyorsa.
Şöyle bir senaryo düşünün: Bir butona basıldığında sayacı 3 kere artırmak istiyorsunuz.
İşte burada krize girdim. Neden? Çünkü count değişkeni, o render'a ait sabit bir değerdir (closure). handleClick fonksiyonu çalıştığında, içindeki her bir setCount(count + 1) ifadesi aslında setCount(0 + 1) demektir. React bu güncellemeleri toplu olarak (batch) işlediği için, sonuncuyu görür ve state'i 1 yapar.
Kurtarıcı: Functional Update Formu
İşte tam bu noktada functional update devreye giriyor. Bu form, state'i güncellerken bir önceki state değerine (prevState) güvenli bir şekilde erişmenizi sağlar. React, güncelleme kuyruğuna aldığı her işlemi sırayla çalıştırır ve bir öncekinin sonucunu bir sonrakine aktarır.
Aynı örneği düzeltelim:
Kullanım Kılavuzu: Ne Zaman Kullanmalı?
1. Mevcut State'e Bağlı Güncellemeler: Sayac artırma/azaltma, array'e eleman ekleme/çıkarma, toggle işlemi.
2. Asenkron Operasyonların İçinden: Bir API çağrısından sonra, bir timeout veya interval içinden state güncellerken.
3. Aynı Event Handler İçinde Birden Fazla Güncelleme: Yukarıdaki sayaç örneğindeki gibi.
Batch Update ile İlişkisi Nedir?
React 17 ve 18'de, React event handler'ları içindeki tüm güncellemeler otomatik olarak toplu işlenir (batched). Yani bir butona tıkladığınızda, handler'daki tüm setState çağrıları tek bir render işleminde birleştirilir. Functional update formu, bu toplu işleme sırasında bile doğru sırayla ve bir önceki değeri referans alarak çalışmayı garanti eder.
Ancak dikkat! setTimeout, promise.then, native event listener'lar gibi React'in kontrolü dışındaki yerlerde, güncellemeler otomatik toplu işlenmeyebilir (React 18'de ReactDOM.flushSync() dışında genelde batch'lenir). İşte buralarda functional update kullanmak hayat kurtarıcıdır.
Sonuç olarak, kuralım şu: Eğer yeni state değerini hesaplamak için mevcut state değerine ihtiyacım varsa, her zaman functional update formunu kullanıyorum. Bu, en temiz ve hataya en kapalı yöntem.
Peki ya siz? React'te state güncellerken hangi formu daha sık kullanıyorsunuz? Hiç batch update sorunu yaşayıp da saatlerinizi debug'a harcadığınız oldu mu? Yorumlarda deneyimlerinizi paylaşın!
Temel kullanımda, state'i doğrudan yeni bir değerle güncelleriz: setIsOpen(true). Bu mükemmel çalışır. Sorun, mevcut state değerine bağlı olan güncellemelerde başlar. Özellikle de bu güncellemeler aynı render cycle içinde, asenkron bir işlemin (event handler, timeout, fetch) içinde art arda yapılıyorsa.
Şöyle bir senaryo düşünün: Bir butona basıldığında sayacı 3 kere artırmak istiyorsunuz.
JavaScript:
function Counter() {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(count + 1); // count şu an 0
setCount(count + 1); // count HÂLÂ 0!
setCount(count + 1); // count YİNE 0!
// Beklenen: 3, Gerçekleşen: 1
};
return (
<button onClick={handleClick}>
Tıkla: {count}
</button>
);
}
İşte burada krize girdim. Neden? Çünkü count değişkeni, o render'a ait sabit bir değerdir (closure). handleClick fonksiyonu çalıştığında, içindeki her bir setCount(count + 1) ifadesi aslında setCount(0 + 1) demektir. React bu güncellemeleri toplu olarak (batch) işlediği için, sonuncuyu görür ve state'i 1 yapar.
İşte tam bu noktada functional update devreye giriyor. Bu form, state'i güncellerken bir önceki state değerine (prevState) güvenli bir şekilde erişmenizi sağlar. React, güncelleme kuyruğuna aldığı her işlemi sırayla çalıştırır ve bir öncekinin sonucunu bir sonrakine aktarır.
Aynı örneği düzeltelim:
JavaScript:
const handleClick = () => {
setCount(prevCount => prevCount + 1); // prevCount = 0, sonuç 1
setCount(prevCount => prevCount + 1); // prevCount = 1, sonuç 2
setCount(prevCount => prevCount + 1); // prevCount = 2, sonuç 3
// Beklenen: 3, Gerçekleşen: 3! 🎉
};
1. Mevcut State'e Bağlı Güncellemeler: Sayac artırma/azaltma, array'e eleman ekleme/çıkarma, toggle işlemi.
JavaScript:
// Diziye eleman ekleme
setItems(prevItems => [...prevItems, newItem]);
// Toggle işlemi
setIsActive(prevState => !prevState);
2. Asenkron Operasyonların İçinden: Bir API çağrısından sonra, bir timeout veya interval içinden state güncellerken.
JavaScript:
useEffect(() => {
const intervalId = setInterval(() => {
setSeconds(prevSeconds => prevSeconds + 1); // Her saniye güvenle artar.
}, 1000);
return () => clearInterval(intervalId);
}, []);
3. Aynı Event Handler İçinde Birden Fazla Güncelleme: Yukarıdaki sayaç örneğindeki gibi.
React 17 ve 18'de, React event handler'ları içindeki tüm güncellemeler otomatik olarak toplu işlenir (batched). Yani bir butona tıkladığınızda, handler'daki tüm setState çağrıları tek bir render işleminde birleştirilir. Functional update formu, bu toplu işleme sırasında bile doğru sırayla ve bir önceki değeri referans alarak çalışmayı garanti eder.
Ancak dikkat! setTimeout, promise.then, native event listener'lar gibi React'in kontrolü dışındaki yerlerde, güncellemeler otomatik toplu işlenmeyebilir (React 18'de ReactDOM.flushSync() dışında genelde batch'lenir). İşte buralarda functional update kullanmak hayat kurtarıcıdır.
Sonuç olarak, kuralım şu: Eğer yeni state değerini hesaplamak için mevcut state değerine ihtiyacım varsa, her zaman functional update formunu kullanıyorum. Bu, en temiz ve hataya en kapalı yöntem.
Peki ya siz? React'te state güncellerken hangi formu daha sık kullanıyorsunuz? Hiç batch update sorunu yaşayıp da saatlerinizi debug'a harcadığınız oldu mu? Yorumlarda deneyimlerinizi paylaşın!