- Konu sahibi
- #1
React’te state setter’ı çağrıldığında mevcut fonksiyon çalışmasının içindeki değişken anında değişmez. Setter sonraki render için güncelleme ister. Çalışmakta olan handler ve onun oluşturduğu callback, ait olduğu render’ın state görüntüsünü korur; daha sonra yeni render tamamlanması eski closure içindeki değişkeni değiştirmez. Bu nedenle setter’dan hemen sonra yapılan
Her render, state değerlerinin o render’a ait bir kopyasıyla çalışır. Aşağıdaki bileşende setter çağrılır; ancak ilk tıklamada hemen sonraki log
Beklenen davranış şudur: İlk tıklamada log eski render’dan gelen
Yeni değeri kullanarak hesap yapmak istiyorsanız değeri önce normal bir değişkende hesaplayın:
Yeni değerin önceki state’e bağlı olduğu durumlarda fonksiyonel güncelleme kullanın. Özellikle aynı olay içinde birden fazla güncelleme yapılıyorsa bu yaklaşım güvenlidir:
Bu örnekte amaç iki artırmaysa,
Değişen state ile dış bir sistemi senkronize edecekseniz, gerekli bağımlılıklarla
Effect içindeki
Karar vermeyi kolaylaştırmak için okuma amacınızı şu şekilde ayırabilirsiniz:
Bu örnekte ref, effect tamamlandığında son commit edilen state ile eşitlenir; render sırasında ref’e yazılmaz. Ref’in
State setter bir değer döndürmez; React’e sonraki render için güncelleme bildirir. Sonucu aynı olayda kullanmanız gerekiyorsa yeni değeri kendiniz hesaplayın. Effect’i ancak dış sistemle senkronizasyon gerekiyorsa kullanın.
React, aynı render sürecinde yapılan güncellemeleri verimli biçimde gruplayabilir. Kodunuzun bu davranışın ayrıntılarına bağlı olmaması için önceki state’i temel alan güncellemelerde fonksiyonel setter kullanın.
Değişiklik ekranda görünmeli ve render başlatmalıysa state kullanın. Değer render başlatmadan callback’ler arasında saklanacaksa ref uygun olabilir; ref’in değişmesi tek başına arayüzü güncellemez.
console.log, aynı olay işleyicisindeki başka bir işlem veya eski bir zamanlayıcı callback’i önceki değeri okuyabilir.State neden aynı fonksiyon içinde değişmez?
Her render, state değerlerinin o render’a ait bir kopyasıyla çalışır. Aşağıdaki bileşende setter çağrılır; ancak ilk tıklamada hemen sonraki log
0 yazar:
Kod:
import { useState } from "react";
export default function Counter() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
console.log("Bu render içindeki değer:", count);
}
return (
<button type="button" onClick={handleClick}>
{count} artır
</button>
);
}
Beklenen davranış şudur: İlk tıklamada log eski render’dan gelen
0 değerini yazar, ardından React yeni render’ı yapar ve ekranda 1 görünür. Bu, setter’ın çalışmadığı anlamına gelmez; log, yeni render’dan önce çalışmıştır.Yeni değeri kullanarak hesap yapmak istiyorsanız değeri önce normal bir değişkende hesaplayın:
Kod:
function handleClick() {
const nextCount = count + 1;
setCount(nextCount);
console.log("Planlanan yeni değer:", nextCount);
}
Yeni değerin önceki state’e bağlı olduğu durumlarda fonksiyonel güncelleme kullanın. Özellikle aynı olay içinde birden fazla güncelleme yapılıyorsa bu yaklaşım güvenlidir:
Kod:
function handleClick() {
setCount(previousCount => previousCount + 1);
setCount(previousCount => previousCount + 1);
}
Bu örnekte amaç iki artırmaysa,
setCount(count + 1) ifadesini iki kez yazmak güvenilir değildir; iki çağrı da aynı eski count değerini temel alabilir. Fonksiyonel güncellemede React her işlemin önceki sonucu üzerinden devam eder.Yeni değeri nerede okumalı?
Değişen state ile dış bir sistemi senkronize edecekseniz, gerekli bağımlılıklarla
useEffect kullanabilirsiniz. Yalnızca tıklama nedeniyle yapılacak bir iş için yeni değeri handler içinde hesaplamak genellikle yeterlidir; her işlem için effect eklemeyin. Aşağıdaki örnek yeni render’ın değerini loglamak içindir:
Kod:
import { useEffect, useState } from "react";
function SearchStatus() {
const [query, setQuery] = useState("");
useEffect(() => {
console.log("Yeni query:", query);
}, [query]);
return (
<label>Arama
<input value={query} onChange={event => setQuery(event.target.value)} />
</label>
);
}
Effect içindeki
query, ilgili render’ın güncel değeridir. Ancak effect’e eksik bağımlılık yazmak eski değerin kullanılmasına yol açabilir. Effect içinde okunan reactive değerleri bağımlılık listesine ekleyin; yalnızca bilinçli olarak sabit kalan değerleri dışarıda bırakın.Karar vermeyi kolaylaştırmak için okuma amacınızı şu şekilde ayırabilirsiniz:
| İhtiyaç | Uygun yaklaşım | Kaçınılacak yaklaşım |
|---|---|---|
| Yeni state’i hemen hesaplamak | Önce nextValue değişkeni oluşturmak | Setter’dan sonra state değişkenini okumak |
| Önceki state’e göre güncellemek | Fonksiyonel setter kullanmak | Eski kapanımdaki değeri tekrar kullanmak |
| Güncelleme sonrasındaki yan etki | Doğru bağımlılıklarla useEffect | Yan etkiyi setter’ın hemen sonrasına koymak |
| Render’lar arasında güncel, ekrana yansımayan değer | Gerekiyorsa useRef | Ref değişimini state render’ı sanmak |
Eski closure ve zamanlayıcılar
setTimeout, event callback’i veya abonelik içinde kullanılan state, callback oluşturulduğu render’ın değerini taşıyabilir. Böyle bir callback’in her güncel değeri görmesi gerekiyorsa effect bağımlılıklarını ve aboneliğin yaşam döngüsünü düzeltin. Güncel değeri render tetiklemeden saklamak gerçekten gerekiyorsa useRef kullanılabilir:
Kod:
import { useEffect, useRef, useState } from "react";
export default function TimerCounter() {
const [count, setCount] = useState(0);
const latestCount = useRef(count);
useEffect(() => {
latestCount.current = count;
}, [count]);
function handleTimer() {
setTimeout(() => console.log(latestCount.current), 1000);
}
return <>
<button type="button" onClick={() => setCount(c => c + 1)}>{count} artır</button>
<button type="button" onClick={handleTimer}>Bir saniye sonra oku</button>
</>;
}
Bu örnekte ref, effect tamamlandığında son commit edilen state ile eşitlenir; render sırasında ref’e yazılmaz. Ref’in
current değerini değiştirmek yeni render başlatmaz. Dolayısıyla ekranda gösterilecek bir değer için ref değil state kullanın.Sık yapılan hatalar
- Setter’dan hemen sonra state değişkeninin yeni olduğunu varsaymak.
- Önceki state’e bağlı işlemlerde doğrudan
setState(value + 1)kullanmak. - Effect bağımlılık listesinden kullanılan state’i çıkarmak.
- Güncel değeri göstermek için ref kullanıp ref değişince bileşenin kendiliğinden render olmasını beklemek.
- Sorunu yalnızca React’in toplu güncellemesine bağlamak; asıl nedenin eski closure veya eksik effect bağımlılığı olup olmadığını kontrol etmemek.
Kaynaklar
Sık sorulan sorular
State setter’ı çağırınca neden hemen return değeri gelmez?
State setter bir değer döndürmez; React’e sonraki render için güncelleme bildirir. Sonucu aynı olayda kullanmanız gerekiyorsa yeni değeri kendiniz hesaplayın. Effect’i ancak dış sistemle senkronizasyon gerekiyorsa kullanın.
React state güncellemelerini neden bazen birleştirir?
React, aynı render sürecinde yapılan güncellemeleri verimli biçimde gruplayabilir. Kodunuzun bu davranışın ayrıntılarına bağlı olmaması için önceki state’i temel alan güncellemelerde fonksiyonel setter kullanın.
State mi, ref mi kullanmalıyım?
Değişiklik ekranda görünmeli ve render başlatmalıysa state kullanın. Değer render başlatmadan callback’ler arasında saklanacaksa ref uygun olabilir; ref’in değişmesi tek başına arayüzü güncellemez.