Bir Seri State Güncellemesini Kuyruğa Almak
Bir state değişkenini değiştirmek, başka bir render’ı kuyruğa alacaktır. Ancak bazen bir sonraki render’ı kuyruğa almadan önce değer üzerinde birden çok işlem gerçekleştirmek isteyebilirsiniz. Bunu yapmakta, React’in batch state güncellemelerini nasıl yaptığını anlamak yardımcı olacaktır.
Bunları öğreneceksiniz
- “Batching (Toplu işleme)” ne demektir ve React bunu birden fazla state güncellemesini yapmak için nasıl kullanır
- Aynı state değişkenine art arda birden fazla güncelleme nasıl yapılır
React state güncellemesini toplu halde (batching) yapar
“+3” butonuna basmanın sayacı üç defa artıracağını çünkü setNumber(number + 1)
fonksiyonunu üç kez çağıracağını düşünebilirsiz:
import { useState } from 'react'; export default function Counter() { const [number, setNumber] = useState(0); return ( <> <h1>{number}</h1> <button onClick={() => { setNumber(number + 1); setNumber(number + 1); setNumber(number + 1); }}>+3</button> </> ) }
Ancak önceki bölümden hatırlayabileceğiniz gibi her render’ın state değeri sabittir, yani setNumber(1)
fonksiyonunu kaç defa çağırırsanız çağırın ilk render’ın olay yöneticisindeki number
değeri her zaman 0
‘dır:
setNumber(0 + 1);
setNumber(0 + 1);
setNumber(0 + 1);
Ancak göze alınması gereken başka bir faktör daha vardır. React, state güncellemelerini yapmadan önce olay yönetecilerindeki tüm kodun çalışmasını bekler. Yeniden render etmenin yalnızca tüm setNumber()
çağrılarından sonra gerçekleşmesinin nedeni budur.
Bu size restoranda sipariş alan bir garsonu hatırlatabilir. Garson siparişinizi alırken ilk yemeğinizi söylediğiniz zaman mutfağa koşmaz! Bunun yerine, siparişinizi tamamlamanıza, üzerinde değişiklik yapmanıza izin verirler. Hatta masadaki diğer insanların da siparişini alırlar.

Rachel Lee Nabors tarafından görselleştirilmiştir.
Bu, yeniden render’lar tetiklemeden birden çok state değişkenini (birden çok bileşende bile) güncellemenizi sağlar. Ancak bu aynı zamanda, kullanıcı arayüzünün (UI) olay yöneteciniz ve içindeki herhangi bir kod tamamlanana kadar güncellenmeyeceği anlamına gelmektedir. Batching (toplu halde) olarak da bilinen bu davranış, React uygulamanızın çok daha hızlı çalışmasını sağlar. Ayrıca React, yalnızca bazı değişkenlerin güncellendiği kafa karıştırıcı “yarı tamamlanmış” render’larla uğraşmaktan da kaçınır.
React tıklama gibi kasıtlı yapılmış birden fazla olayı toplu olarak işlemez her tıklama ayrı olarak işlenir. React’in bu gruplamayı genel olarak yalnızca güvenli olduğunu düşündüğü durumlarda yaptığından emin olabilirsiniz. Böylelikle örneğin, eğer bir butona tıklamak formu devre dışı bırakıyor ise, butona ikinci defa tıklamak formu tekrar göndermez.
Bir sonraki render’dan önce aynı state’i birden çok defa güncellemek
Şimdi vereceğimiz örnek pek alışılmadık bir durumdur ancak aynı state değişkenini bir sonraki render’dan önce birden çok defa güncellemek isterseniz, setNumber(number + 1)
gibi sonraki state değerini iletmek yerine setNumber(n => n + 1)
gibi bir sonraki state’i kuyruktaki önceki değere göre hesaplayan bir fonksiyon iletebilirsiniz. Böylelikle React’e state değerini değiştirmek yerine “state değeri ile bir şey yap” diyebilirsiniz.
Şimdi sayacı tekrar artırmayı deneyin:
import { useState } from 'react'; export default function Counter() { const [number, setNumber] = useState(0); return ( <> <h1>{number}</h1> <button onClick={() => { setNumber(n => n + 1); setNumber(n => n + 1); setNumber(n => n + 1); }}>+3</button> </> ) }
Burada, n => n + 1
ifadesi güncelleyici fonksiyon olarak adlandırılır. Bu fonksiyonu bir state setter’a ilettiğiniz zaman:
- React, bu fonksiyonu olay yöneticisindeki tüm kodlar çalıştıktan sonra işlemek üzere kuyruğa alır.
- Bir sonraki render esnasında React, kuyruktaki tüm işlemleri yapar ve size güncellenmiş son state’i verir.
setNumber(n => n + 1);
setNumber(n => n + 1);
setNumber(n => n + 1);
React’in olay yönetecisini yürütürken bu kod satırlarında nasıl çalıştığını aşağıdaki gibi anlatabiliriz:
setNumber(n => n + 1)
:n => n + 1
ifadesi bir fonksiyondur. React bu ifadeyi kuyruğa alır.setNumber(n => n + 1)
:n => n + 1
ifadesi bir fonksiyondur. React bu ifadeyi kuyruğa alır.setNumber(n => n + 1)
:n => n + 1
ifadesi bir fonksiyondur. React bu ifadeyi kuyruğa alır.
Sonraki render’da useState
‘i çağırdığınız zaman React, kuyruktaki işlemleri yapar. Önceki number
state’i 0
‘dı, dolayısıyla React bunu n
argümanı olarak ilk güncelleyici fonksiyona iletir. Ardından, önceki güncelleyici fonksiyonunuzun döndürdüğü değeri alır ve bu değeri bir sonraki güncelleyici fonksiyona n
olarak iletir ve bu böyle devam eder:
kuyruktaki güncelleme | n | döndürülen değer |
---|---|---|
n => n + 1 | 0 | 0 + 1 = 1 |
n => n + 1 | 1 | 1 + 1 = 2 |
n => n + 1 | 2 | 2 + 1 = 3 |
React 3
değerini son sonuç olarak saklar ve useState
‘den döndürür.
Bu yüzden yukarıdaki örnekte “+3” butonuna tıklamak, değeri doğru şekilde 3 artıracaktır.
State’i değiştirdikten sonra güncellerseniz ne olur
Peki ya olay yöneticisi? number
‘ın değeri sonraki render’da ne olacaktır?
<button onClick={() => {
setNumber(number + 5);
setNumber(n => n + 1);
}}>
import { useState } from 'react'; export default function Counter() { const [number, setNumber] = useState(0); return ( <> <h1>{number}</h1> <button onClick={() => { setNumber(number + 5); setNumber(n => n + 1); }}>Increase the number</button> </> ) }
Aşağıda bu olay yönetecisinin React’e ne yapması gerektiğini söylediğini görebilirsiniz:
setNumber(number + 5)
:number
ın değeri0
‘dır, yanisetNumber(0 + 5)
. React kuyruğa ”5
ile değiştir” ifadesini alır.setNumber(n => n + 1)
:n => n + 1
ifadesi bir güncelleyici fonksiyondur. React o fonksiyonu kuyruğa alır.
Sonraki render esnasında React, state kuyruğundan geçer:
kuyruktaki güncelleme | n | döndürülen değer |
---|---|---|
“replace with 5 ” | 0 (unused) | 5 |
n => n + 1 | 5 | 5 + 1 = 6 |
React 6
değerini son sonuç olarak saklar ve useState
‘den döndürür.
State’i güncelledikten sonra değiştirirseniz ne olur
Bir örnek daha deneyelim. Sonraki render’da number
‘ın değeri ne olacaktır?
<button onClick={() => {
setNumber(number + 5);
setNumber(n => n + 1);
setNumber(42);
}}>
import { useState } from 'react'; export default function Counter() { const [number, setNumber] = useState(0); return ( <> <h1>{number}</h1> <button onClick={() => { setNumber(number + 5); setNumber(n => n + 1); setNumber(42); }}>Increase the number</button> </> ) }
Bu olay yönetecisi çalışırken React’in kod satırlarında nasıl çalıştığını aşağıda görebilirsiniz:
setNumber(number + 5)
:number
‘ın değeri0
‘dır, yanisetNumber(0 + 5)
. React kuyruğa ”5
ile değiştir” ifadesini alır.setNumber(n => n + 1)
:n => n + 1
ifadesi bir güncelleyici fonksiyondur. React o fonksiyonu kuyruğa alır.setNumber(42)
: React kuyruğa ”42
ile değiştir” ifadesini alır.
Sonraki render esnasında React, state kuyruğundan geçer:
kuyruktaki güncelleme | n | döndürülen değer |
---|---|---|
“replace with 5 ” | 0 (unused) | 5 |
n => n + 1 | 5 | 5 + 1 = 6 |
“replace with 42 ” | 6 (unused) | 42 |
React 42
değerini son sonuç olarak saklar ve useState
‘den döndürür.
Özetlemek gerekirse, state setter olan setNumber
‘a ne ilettiğinizi şu şekilde düşünebilirsiniz:
- Bir güncelleyici fonksiyon (örneğin
n => n + 1
) kuyruğa eklenir. - Herhangi başka bir değer (örneğin number
5
) zaten kuyrukta olanları yok sayarak kuyruğa ”5
ile değiştir” ifadesini ekler.
Olay yöneticisi tamamlandıktan sonra, React yeniden bir render tetikleyecektir. Yeniden render esnasında, React kuyruktaki işlemleri yapacaktır. Güncelleyici fonksiyonlar render etme esnasında çalışırlar yani güncelleyici fonksiyonlar saf olmalıdır ve sadece sonucu döndürmelidirler. Güncelleyici fonksiyonlar içinde state’i değiştirmeyi ya da başka yan etkiler çalıştırmayın. Strict Mode’da React, hataları bulmanıza yardımcı olmak için her güncelleyici fonksiyonu iki defa çalıştırır (ancak ikinci sonucu göz ardı eder).
Sık kullanılan adlandırmalar
Güncelleyici fonksiyon değişkenini, karşılık gelen state değişkeninin ilk harfleriyle adlandırmak yaygın olarak kullanılır:
setEnabled(e => !e);
setLastName(ln => ln.reverse());
setFriendCount(fc => fc * 2);
Daha ayrıntılı bir kod yapısını tercih ediyorsanız, başka bir yaygın kural, setEnabled(enabled => !enabled)
gibi state değişkeni adını ya da setEnabled(prevEnabled => !prevEnabled)
gibi bir önad kullanmaktır.
Özet
- State’i değiştirmek mevcut render’daki değişkeni değiştirmez, yeni bir render talep eder.
- React, olay yönetecileri çalışmayı bitirdikten sonra state güncellemelerini yapar. Bu duruma batching (toplu halde) denir.
- Bir olayda bazı state’leri birden çok defa güncellemek için
setNumber(n => n + 1)
güncelleyici fonksiyonu kullanılır.
Problem 1 / 2: Sayaçları düzeltin
Kullanıcının aynı anda bir sanat eseri için birden çok sipariş göndermesine olanak sağlayan bir sanat marketi üzerinde çalışıyorsunuz. Kullanıcı her “Buy (Satın al)” butonuna bastığında, “Pending (Bekleniyor)” sayacı bir artmalıdır. Üç saniye sonra “Pending (Bekleniyor)” sayacı bir azalmalı ve “Completed (Tamamlandı)” sayacı bir artmalıdır.
Ancak “Pending” sayacı çalışması gerektiği gibi çalışmamaktadır. “Buy” butonuna basıldığında, sayaç -1
olmaktadır (ki bu imkansızdır!). Aynı zamanda butona iki defa hızlıca tıklarsanız, iki sayaç da tahmin edilemeyecek bir şekilde çalışmaktadır.
Sizce bu niye olmakta? İki sayacı da düzeltin.
import { useState } from 'react'; export default function RequestTracker() { const [pending, setPending] = useState(0); const [completed, setCompleted] = useState(0); async function handleClick() { setPending(pending + 1); await delay(3000); setPending(pending - 1); setCompleted(completed + 1); } return ( <> <h3> Pending: {pending} </h3> <h3> Completed: {completed} </h3> <button onClick={handleClick}> Buy </button> </> ); } function delay(ms) { return new Promise(resolve => { setTimeout(resolve, ms); }); }