Anlık Durum
Durum değişkenleri, okuyup yazabileceğiniz normal JavaScript değişkenleri gibi görünebilir. Ancak durum daha çok bir anlık görüntü gibi davranır. Durumu ayarlamak, zaten sahip olduğunuz durum değişkenini değiştirmez, bunun yerine yeniden render'ı tetikler.
- Durum ayarlamanın yeniden render'ları nasıl tetiklediği
- Durumun ne zaman ve nasıl güncellendiği
- Durumun ayarlandıktan hemen sonra neden güncellenmediği
- Olay işleyicilerin durumu nasıl bir "anlık görüntü" olarak eriştiği
Durum Ayarlama Yeniden Render'ları Tetikler {/setting-state-triggers-renders/}
Kullanıcı arayüzünüzü, tıklama gibi bir kullanıcı olayına doğrudan yanıt olarak değişiyormuş gibi düşünebilirsiniz. React'te bu, bu zihinsel modelden biraz farklı çalışır. Önceki sayfada, durum ayarlamanın React'ten yeniden render talep ettiğini
gördünüz. Bu, bir arayüzün olaya tepki vermesi için durumun güncellenmesi gerektiği anlamına gelir.
Bu örnekte, "gönder" butonuna bastığınızda, setIsSent(true)
React'e UI'yi yeniden render etmesini söyler:
import { useState } from 'react';
export default function Form() {
const [isSent, setIsSent] = useState(false);
const [message, setMessage] = useState('Merhaba!');
if (isSent) {
return <h1>Mesajınız yolda!</h1>
}
return (
<form onSubmit={(e) => {
e.preventDefault();
setIsSent(true);
sendMessage(message);
}}>
<textarea
placeholder="Mesaj"
value={message}
onChange={e => setMessage(e.target.value)}
/>
<button type="submit">Gönder</button>
</form>
);
}
function sendMessage(message) {
// ...
}
label, textarea { margin-bottom: 10px; display: block; }
Butona tıkladığınızda olanlar:
onSubmit
olay işleyici çalıştırılır.setIsSent(true)
isSent
'itrue
olarak ayarlar ve yeni bir render kuyruğa alır.- React, yeni
isSent
değerine göre bileşeni yeniden render eder.
Durum ve render arasındaki ilişkiye daha yakından bakalım.
Render Zamanında Bir Anlık Görüntü Alır {/rendering-takes-a-snapshot-in-time/}
"Render" işlemi
, React'in bileşeninizi çağırdığı anlamına gelir; bu, bir işlevdir. O işlevden dönen JSX, zaman içindeki UI'nin bir anlık görüntüsü gibidir. Props'ları, olay işleyicileri ve yerel değişkenleri, tümü o render sırasında durumunu kullanarak hesaplanmıştır.
Bir fotoğraf ya da film karesi gibi olmasa da, döndürdüğünüz UI "anlık görüntüsü" etkileşimlidir. Girdi yanıtında ne olacağının belirtildiği olay işleyicileri gibi mantığı içerir.
React, bir bileşeni yeniden render ettiğinde:
- React, işlevinizi tekrar çağırır.
- İşleviniz, yeni bir JSX anlık görüntüsü döner.
- React, ardından ekranı işlevinizin döndürdüğü anlık görüntü ile günceller.
Bir bileşenin hafızası olarak, durum, işleviniz döndükten sonra kaybolan normal bir değişken gibi değildir. Durum aslında React'in kendisinde, sanki bir rafın üzerinde yaşıyor gibi! React, bileşeninizi çağırdığında, size o belirli render için durumun bir anlık görüntüsünü verir. Bileşeniniz, bu render için hesaplanmış taze bir props ve olay işleyici seti ile JSX içinde bir UI anlık görüntüsü döner, tümü o renderden elde edilen durum değerleri kullanılarak!
Bu çalışmanın nasıl olduğunu göstermek için küçük bir deney yapalım.
Bu örnekte, "+3" butonuna tıkladığınızda sayacın üç kez artacağını bekleyebilirsiniz çünkü setNumber(number + 1)
üç kez çağrılır.
"+3" butonuna tıkladığınızda ne olduğunu görün:
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>
</>
)
}
button { display: inline-block; margin: 10px; font-size: 20px; }
h1 { display: inline-block; margin: 10px; width: 30px; text-align: center; }
Dikkat edin ki number
her tıklamada yalnızca bir kez artıyor!
Durumu ayarlamak, sadece bir sonraki render için değiştirir. İlk render sırasında, number
0
idi. Bu, o renderin onClick
işleyicisinde number
değerinin hala 0
olduğu anlamına gelir, setNumber(number + 1)
çağrıldıktan sonra bile:
<button onClick={() => {
setNumber(number + 1);
setNumber(number + 1);
setNumber(number + 1);
}}>+3</button>
Bu butonun tıklama işleyicisi React'e şunları söylemektedir:
setNumber(number + 1)
:number
0
olduğu içinsetNumber(0 + 1)
.- React, bir sonraki renderda
number
'ı1
olarak değiştirmek için hazırlanır.
- React, bir sonraki renderda
setNumber(number + 1)
:number
0
olduğu içinsetNumber(0 + 1)
.- React, bir sonraki renderda
number
'ı1
olarak değiştirmek için hazırlanır.
- React, bir sonraki renderda
setNumber(number + 1)
:number
0
olduğu içinsetNumber(0 + 1)
.- React, bir sonraki renderda
number
'ı1
olarak değiştirmek için hazırlanır.
- React, bir sonraki renderda
setNumber(number + 1)
'i üç kez çağırmış olsanız bile, bu renderin olay işleyicisinde number
her zaman 0
'dır, bu yüzden durumu 1
olarak üç kez ayarlarsınız. Bu nedenle, olay işleyiciniz tamamlandığında, React bileşeni number
eşit 1
olacak şekilde yeniden render eder.
Kodunuzdaki durum değişkenlerini değerleriyle zihinsel olarak değiştirerek bu durumu görselleştirebilirsiniz. Çünkü number
durum değişkeni bu render için 0
olduğundan, olay işleyicisi şöyle görünür:
<button onClick={() => {
setNumber(0 + 1);
setNumber(0 + 1);
setNumber(0 + 1);
}}>+3</button>
Sonraki renderda, number
1
'dir, bu yüzden o renderin tıklama işleyicisi şöyle görünür:
<button onClick={() => {
setNumber(1 + 1);
setNumber(1 + 1);
setNumber(1 + 1);
}}>+3</button>
Bu, butona tekrar tıkladığınızda sayacın önce 2
, ardından 3
olacağı ve bu şekilde devam edeceği anlamına gelir.
Zamana Göre Durum {/state-over-time/}
İyi, bu eğlenceliydi. Bu butona tıkladığınızda neyin uyarı vereceğini tahmin etmeye çalışın:
import { useState } from 'react';
export default function Counter() {
const [number, setNumber] = useState(0);
return (
<>
<h1>{number}</h1>
<button onClick={() => {
setNumber(number + 5);
alert(number);
}}>+5</button>
</>
)
}
button { display: inline-block; margin: 10px; font-size: 20px; }
h1 { display: inline-block; margin: 10px; width: 30px; text-align: center; }
Eğer önceki değişim yöntemini kullanırsanız, uyarının "0" olduğunu tahmin edebilirsiniz:
setNumber(0 + 5);
alert(0);
Ama uyarıyı bir zamanlayıcıya koyarsanız, böylece yalnızca bileşen yeniden render edildikten sonra ateşlenirse? "0" mı yoksa "5" mi der? Bir tahminde bulunun!
import { useState } from 'react';
export default function Counter() {
const [number, setNumber] = useState(0);
return (
<>
<h1>{number}</h1>
<button onClick={() => {
setNumber(number + 5);
setTimeout(() => {
alert(number);
}, 3000);
}}>+5</button>
</>
)
}
button { display: inline-block; margin: 10px; font-size: 20px; }
h1 { display: inline-block; margin: 10px; width: 30px; text-align: center; }
Şaşırdınız mı? Eğer önceki değişim yöntemini kullanırsanız, uyarıya geçen durumun "anlık görüntüsünü" görebilirsiniz.
setNumber(0 + 5);
setTimeout(() => {
alert(0);
}, 3000);
React'te kayıtlı durum, uyarı çalıştığında değişmiş olabilir, ancak kullanıcı etkileşimi sırasında durumun anlık görüntüsü kullanılarak planlanmıştı!
Bir durum değişkeninin değeri bir render içinde asla değişmez, hatta olay işleyicisinin kodu asenkron bile olsa. O renderin onClick
'inde, number
değeri, setNumber(number + 5)
çağrıldıktan sonra bile 0
olmaya devam eder. Değeri, React'in UI'nin anlık görüntüsünü almadan önce "sabittir".
Bu, olay işleyicilerinizi zamanlama hatalarına karşı daha az duyarlı hale getirir. Aşağıda beş saniyelik bir gecikme ile bir mesaj gönderen bir form örneği vardır. Bu senaryoyu hayal edin:
- "Gönder" butonuna basıyorsunuz ve "Merhaba"yı Alice'e gönderiyorsunuz.
- Beş saniyelik gecikme sona ermeden önce, "Kime" alanının değerini "Bob" olarak değiştiriyorsunuz.
Uyarının neyi göstermesini bekliyorsunuz? "Alice'e Merhaba dediniz" mi? Yoksa "Bob'a Merhaba dediniz" mi? Bildiklerinize dayanarak bir tahminde bulunun ve ardından bunu deneyin:
import { useState } from 'react';
export default function Form() {
const [to, setTo] = useState('Alice');
const [message, setMessage] = useState('Merhaba');
function handleSubmit(e) {
e.preventDefault();
setTimeout(() => {
alert(`You said ${message} to ${to}`);
}, 5000);
}
return (
<form onSubmit={handleSubmit}>
<label>
Kime:{' '}
<select
value={to}
onChange={e => setTo(e.target.value)}>
<option value="Alice">Alice</option>
<option value="Bob">Bob</option>
</select>
</label>
<textarea
placeholder="Mesaj"
value={message}
onChange={e => setMessage(e.target.value)}
/>
<button type="submit">Gönder</button>
</form>
);
}
label, textarea { margin-bottom: 10px; display: block; }
React, durum değerlerini bir renderin olay işleyicileri içinde "sabitlemektedir." Kod çalışırken durumun değişip değişmediği konusunda endişelenmenize gerek yok.
Ama yeniden render'dan önce en son durumu okumak istiyorsanız? Durum güncelleyici işlevi
kullanmak isteyeceksiniz, bir sonraki sayfada detaylandırılacak!
- Durum ayarlamak, yeni bir render talep eder.
- React, durumu bileşeninizin dışında, sanki bir rafın üzerinde saklar.
useState
fonksiyonunu çağırdığınızda, React, o render için durumun bir anlık görüntüsünü verir.- Değişkenler ve olay işleyicileri "yeniden render" sürekliliğini sağlamaz. Her render kendi olay işleyicilerine sahiptir.
- Her render (ve içindeki işlevler) her zaman o render için React'in verdiği durum anlık görüntüsünü "görmektedir."
- Olay işleyicilerinde durumu zihinsel olarak değiştirebilirsiniz, tıpkı render edilen JSX'e düşündüğünüz gibi.
- Geçmişte oluşturulan olay işleyicileri, oluşturuldukları renderdeki durum değerlerini taşır.
Trafik Işığını Uygulama {/implement-a-traffic-light/}
Buton tıklandığında değişen bir yaya ışığı bileşeni buradadır:
import { useState } from 'react';
export default function TrafficLight() {
const [walk, setWalk] = useState(true);
function handleClick() {
setWalk(!walk);
}
return (
<>
<button onClick={handleClick}>
Değiştir: {walk ? 'Dur' : 'Yürü'}
</button>
<h1 style={{
color: walk ? 'koyu yeşil' : 'koyu kırmızı'
}}>
{walk ? 'Yürü' : 'Dur'}
</h1>
</>
);
}
h1 { margin-top: 20px; }
Olay işleyicisinde bir alert
ekleyin. Işık yeşil olduğunda ve "Yürü" dediğinde, butona tıklandığında "Dur sonraki" demelidir. Işık kırmızı olduğunda ve "Dur" dediğinde, butona tıklandığında "Yürü sonraki" demelidir.
alert
'i setWalk
çağrısından önce mi yoksa sonra mı koymanın bir anlamı var mı?
Uyarınız şöyle görünmelidir:
import { useState } from 'react';
export default function TrafficLight() {
const [walk, setWalk] = useState(true);
function handleClick() {
setWalk(!walk);
alert(walk ? 'Dur sonraki' : 'Yürü sonraki');
}
return (
<>
<button onClick={handleClick}>
Değiştir: {walk ? 'Dur' : 'Yürü'}
</button>
<h1 style={{
color: walk ? 'koyu yeşil' : 'koyu kırmızı'
}}>
{walk ? 'Yürü' : 'Dur'}
</h1>
</>
);
}
h1 { margin-top: 20px; }
alert
'i setWalk
çağrısından önce mi yoksa sonra mı koyduğunuz fark etmez. O render 'dan gelen walk
değerinin sabit olduğu bir durumdur. setWalk
çağrılması yalnızca bir sonraki render için durumu değiştirecektir, ancak önceki render'in olay işleyicisini etkilemeyecektir.
Bu satır, ilk başta karşıt bir mantık gibi görünebilir:
alert(walk ? 'Dur sonraki' : 'Yürü sonraki');
Ama şöyle okunursa mantıklıdır: "Eğer trafik ışığı 'Şimdi Yürü' diyorsa, mesaj 'Dur sonraki' olmalıdır." Olay işleyicinizin içindeki walk
değişkeni, o renderin walk
değerine eşittir ve değişmez.
Doğru olduğunu doğrulamak için değişim yöntemini uygulayabilirsiniz. walk
doğruysa, şunu elde edersiniz:
<button onClick={() => {
setWalk(false);
alert('Dur sonraki');
}}>
Değiştir: Dur
</button>
<h1 style={{color: 'koyu yeşil'}}>
Yürü
</h1>
Bu nedenle "Dur'a Değiştir" butonuna tıkladığınızda walk
değeri false
olarak ayarlanmış bir render kuyruğuna girer ve "Dur sonraki" uyarısını verir.