State ve Lifecycle
Önceki konularda gördüğümüz saat örneğini düşünün.
Şu ana kadar UI’ı güncellemenin tek bir yolunu öğrendik.
Görüntülenen çıktıyı değiştirmek için ReactDOM.render()
fonksiyonunu her saniye çağırıyorduk:
function tick() {
const element = (
<div>
<h1>Merhaba Dünya!</h1>
<h2>Saat şu anda {new Date().toLocaleTimeString()}</h2>
</div>
);
ReactDOM.render(
element,
document.getElementById('root')
);
}
setInterval(tick, 1000);
Bu bölümde, Clock
componentini yeniden kullanımının doğru yöntemini öğreneceğiz.
Zamanlayıcıyı kendisi ayarlayacak ve her saniyede bir güncelleme yapacaktır.
Clock
componentinin nasıl göründüğünü yazmakla başlayabiliriz:
function Clock(props) {
return (
<div>
<h1>Merhaba Dünya!</h1>
<h2>Saat şu anda {props.date.toLocaleTimeString()}</h2>
</div>
);
}
function tick() {
ReactDOM.render(
<Clock date={new Date()} />,
document.getElementById('root')
);
}
setInterval(tick, 1000);
Daha önce anlatılmıştı fakat tekrar etmekte fayda var.
Clock
componenti çağrılırken attribute olarak date={new Date()}
gönderiliyor.
Bu attribute, Clock
componentine parametre olarak gelir ve adı props
tur.
Clock
componentine bu tarihi yazdırmak içinde props.date
şeklinde kullanıyoruz.
Bunu bir kez yazmak ve Clock
component güncellemesini kendisi gerçekleştirsin isteriz:
ReactDOM.render(
<Clock />,
document.getElementById('root')
);
Bunu uygulamak için Clock
componentine state
eklemeliyiz.
Stateler propslara benzer, ancak tamamen component tarafından kontrol edilir.
Daha önce belirttiğimiz gibi, class olarak tanımlanan componentlerin bazı ilave özellikler var.
State yalnızca classlar için kullanılabilen bir özelliktir.
Bir Fonksiyonun Class'a Dönüştürülmesi
Bu kısıma kadar componentleri fonksiyon olarak tanımlamıştık. Fakat React’ta class olarak oluşturmaya alışmak daha doğru olacaktır. Şimdi adım adım bir fonksiyonun class’a çevrilme işlemini inceleyelim.
Clock
fonksiyon componentini aşağıdaki adımlarla class componentine dönüştürebiliriz.
-
React.Component
ine extend eden aynı ada sahip bir ES6 classı oluşturalım. (class Clock extends React.Component
) -
Buna
render()
adı verilen boş bir fonksiyon ekleyin. -
Fonksiyonun içerisindeki kodları
render()
ın içerisine taşıyın. -
props
un adınıthis.props
olarak değiştirin.
class Clock extends React.Component {
render() {
return (
<div>
<h1>Merhaba Dünya!</h1>
<h2>Saat şu anda {this.props.date.toLocaleTimeString()}</h2>
</div>
);
}
}
Fonksiyon component yerine class component kullanımına alışın. Üst kısımdaki adımları anlamadıysanız tekrar tekrar okumanızda fayda var.
Clock
componenti bir fonksiyon yerine bir class olarak tanımlanmış oldu.
Bu, state
ve lifecycle
(yaşam döngüsü) gibi ek özellikleri kullanmamızı sağlar.
Bir Class'a State Eklemek
Bir class’a state eklemek için aşağıdaki 3 adımı dikkatlice inceleyiniz.
this.props.date
satırınıthis.state.date
olarak güncelleyin:
class Clock extends React.Component {
render() {
return (
<div>
<h1>Merhaba Dünya!</h1>
<h2>Saat şu anda {this.state.date.toLocaleTimeString()}</h2>
</div>
);
}
}
- Class componentimizin içerisine state’i ilk anda tanımlayan bir constructor oluşturalım.
Constructor fonksiyonu, bir classla beraber oluşturulan, nesnenin oluşturulması ve başlatılması için özel bir fonskiyondur. Bir classta “constructor” ismiyle yalnızca bir tane özel fonskiyon olabilir. Class oluşturulduğu anda içerisindeki kodlar çalışır.
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
render() {
return (
<div>
<h1>Merhaba Dünya!</h1>
<h2>Saat şu anda {this.state.date.toLocaleTimeString()}</h2>
</div>
);
}
}
props
un constructorden nasıl alındığına dikkat ediniz:
constructor
e parametre olarak props
ekleyip, içerisine super(props);
satırını ekliyoruz.
constructor(props) {
super(props);
this.state = {date: new Date()};
}
Class componenleri her zaman constructore props
ile çağırmalıdır.
<Clock />
componentinden date
attribute’ünü kaldırın:
ReactDOM.render(
//eski hali = <Clock date={new Date()} />
<Clock />,
document.getElementById('root')
);
Zamanlayıcı kodunu daha sonra componentin kendisine geri ekleyeceğiz.
Şu anda sonuç şöyle görünüyor:
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
render() {
return (
<div>
<h1>Merhaba Dünya!</h1>
<h2>Saat şu anda {this.state.date.toLocaleTimeString()}</h2>
</div>
);
}
}
ReactDOM.render(
<Clock />,
document.getElementById('root')
);
Şimdi, Clock
i kendi zamanlayıcısını oluşturup her saniyede bir güncelleyeceğiz.
Bir Classa Lifecycle Fonksiyonları Ekleme
Birçok componente sahip uygulamalarda, componentler yok edildiğinde alınan kaynakları boşaltmak çok önemlidir.
Clock
, DOM’a ilk defa çağırıldığında bir zamanlayıcı ayarlamak istiyoruz. Buna React’te mounting
denir.
Ayrıca, Saat componentinden üretilen DOM kaldırıldığında,
bu zamanlayıcıyı temizlemek istiyoruz.
Buna ise React’te unmounting
denir.
Bir component DOM’a yazdırılıp kaldırıldığında bazı kod satırlarını çalıştırmak için class componente özel yöntemler ekleyebiliriz:
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
componentDidMount() {
//Component oluşturulduğunda çalışacak fonksiyon
}
componentWillUnmount() {
//Component kaldırıldığında çalışacak fonksiyon
}
render() {
return (
<div>
<h1>Merhaba Dünya!</h1>
<h2>Saat şu anda {this.state.date.toLocaleTimeString()}</h2>
</div>
);
}
}
Bu fonksiyonlara lifecycle hooks
(yaşam döngüsü kancaları) adı verilir.
Component çıktısı DOM’a aktarıldıktan sonra componentDidMount()
fonksiyonu çalışır.
Bu, zamanlayıcı ayarlamak için iyi bir yerdir:
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
() => this.tick()
kullanımı ES6
ile gelen kısa fonksiyon kullanımıdır.
ES5
‘teki kullanımı function() { return this.tick();
}</i>
Zamanlayıcı this
in üzerine nasıl kaydettiğimizi unutmayın.
Zamanlayıcıyı componentWillUnmount()
fonksiyonunda temizleyeceğiz:
componentWillUnmount() {
clearInterval(this.timerID);
}
Son olarak, Clock
componentini her saniyede bir çalışacağı tick()
adında bir fonksiyon oluşturacağız.
Bu fonksiyon state’eki tarihi this.setState()
kullanarak güncelleyecektir.
Daha önceki konularda props
ların yalnızca okunabilir olduğunu ve güncellenmemesi gerektiğinin notunu düşmüştük.
Bizler (this.setState()
ile) state
‘i değiştireceğiz, React ise o state’i kullanan yerleri (props
ları) kendisi güncelleyecek.
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
componentWillUnmount() {
clearInterval(this.timerID);
}
//Fonksiyon tanımlarken başına "function" yazmadığımıza dikkat edin.
//React componentleri içerisinde fonksiyon tanımlamak istediğimizde direkt isim vererek yazıyoruz.
tick() {
this.setState({
date: new Date()
});
}
render() {
return (
<div>
<h1>Merhaba Dünya!</h1>
<h2>Saat şu anda {this.state.date.toLocaleTimeString()}</h2>
</div>
);
}
}
ReactDOM.render(
<Clock />,
document.getElementById('root')
);
Neler olup bittiğini hızla özetleyelim:
1) ReactDOM.render()
içerisinde <Clock />
componenti çağırıldığında, React Clock
componentinin constructorünü çağırır.
Clock
componentinin geçerli saati göstermesi gerektiği için this.state
‘i geçerli saat ile başlatır.
Daha sonra bu state güncellenecek.
2) React sonra Clock
componentinin render()
fonksiyonunu çağırır.
React, ekranda neyin görüntülenmesi gerektiğini öğrenir.
Daha sonra, Clock
componentinin çıktısı ile eşleşecek şekilde DOM’u güncelleştirir.
3) Clock
componentinin çıktısı DOM’a eklendiğinde, React, componentDidMount()
fonksiyonunu çağırır.
Her saniye çalışacak olan tick()
fonksiyonunu timerID
de tutar.
4) Her saniye tarayıcı tick()
fonksiyonunu çağırır.
Clock
componentinin içinde, geçerli saati içeren bir nesneyle setState()
i çağırarak bir UI güncellemesi içinsetInterval()
fonksiyonunu hazırlar.
setState()
çağrısı sayesinde React, statin değiştiğini bilir ve ekranda ne olması gerektiğini öğrenmek için tekrar render()
eder.
Bu sefer render()
yöntemindeki this.state.date
güncellenmiş olacaktır. React DOM’u buna göre günceller.
5) Clock
componenti DOM’dan kaldırıldıysa, React, timerID
durduğu anda componentWillUnmount()
fonksiyonunu çağırır.
State'i Doğru Kullanmak
setState()
hakkında bilmeniz gereken üç şey vardır.
State'i Doğrudan Değiştirmeyin
Örneğin, bu bir componenti yeniden oluşturmaz:
// Yanlış Kullanım
this.state.comment = 'Merhaba';
setState()
i şu şekilde kullanın:
// Doğru Kullanım
this.setState({comment: 'Merhaba'});
this.state
i atayabileceğiniz tek yer constructor
dür.
State Güncellemeleri Asenkron Olabilir
React, birden fazla setState()
çağrısını performans için tek bir güncelleme haline getirebilir.
Çünkü this.props
ve this.state
eşzamansız olarak güncellenebilir.
Örneğin, bu sayaç güncellemesi başarısız olabilir:
// Yanlış Kullanım
this.setState({
counter: this.state.counter + this.props.increment,
});
Bunu düzeltmek için, bir objeden ziyade bir fonksiyonu kabul eden ikinci bir setState()
formunu kullanın.
Bu işlev önceki durumu ilk argüman olarak, güncelleme ikinci argüman olarak uygulandığında sahne alacaktır:
// Doğru Kullanım
this.setState((prevState, props) => ({
counter: prevState.counter + props.increment
}));
Yukarıdaki örnekte ES6 ile gelen ok fonksiyonu kullandık fakat normal fonksiyonlarla da yapabilirdik:
// Doğru Kullanım
this.setState(function(prevState, props) {
return {
counter: prevState.counter + props.increment
};
});
State'leri Toplu Güncelleştirmek
setState()
öğesini çağırdığınızda, React state’i birleştirir. Bu yüzden birden fazla state aynı anda güncellenebilir.
constructor(props) {
super(props);
this.state = {
posts: [],
comments: []
};
}
Ayrıca bunları ayrı setState()
çağrılarıyla bağımsız olarakta güncelleyebilirsiniz:
componentDidMount() {
fetchPosts().then(response => {
this.setState({
posts: response.posts
});
});
fetchComments().then(response => {
this.setState({
comments: response.comments
});
});
}
setState
i kullandığımızda tüm state’ler değiştirilmez. Yalnızca belirtilen state güncellenir.
Yani this.setState({comments})
şeklinde kullanıldığında sadece this.state.comments
güncellenir, this.state.posts
güncellenmez.