State ve Yaşam Döngüsü
These docs are old and won’t be updated. Go to react.dev for the new React docs.
These new documentation pages teach modern React and include live examples:
Bu sayfada, state kavramı ve React bileşenlerinin yaşam döngüsü tanıtılacaktır. Bileşen API’si hakkında ayrıntılı bilgi için, bu dokümana bakabilirsiniz.
Önceki bölümlerde bahsettiğimiz, analog saat örneğini ele alacağız. Hatırlayacağınız gibi, Elementlerin Render Edilmesi bölümünde, kullanıcı arayüzünün yalnızca tek yönlü güncellenmesine yer vermiştik. Bunu root.render() metodu ile gerçekleştirebiliyorduk:
const root = ReactDOM.createRoot(document.getElementById('root'));
function tick() {
const element = (
<div>
<h1>Hello, world!</h1>
<h2>It is {new Date().toLocaleTimeString()}.</h2>
</div>
);
root.render(element);}
setInterval(tick, 1000);Bu bölümde ise, Clock bileşenini nasıl sarmalayacağımıza ve tekrar kullanılabilir hale getireceğimize değineceğiz. Bu bileşen, kendi zamanlayıcısını başlatacak ve her saniye kendisini güncelleyecek.
Öncelikle Clock’u, ayrı bir bileşen halinde sarmalayarak görüntüleyelim:
const root = ReactDOM.createRoot(document.getElementById('root'));
function Clock(props) {
return (
<div> <h1>Hello, world!</h1> <h2>It is {props.date.toLocaleTimeString()}.</h2> </div> );
}
function tick() {
root.render(<Clock date={new Date()} />);}
setInterval(tick, 1000);Güzel görünüyor ancak bu aşamada kritik bir gereksinimi atladık: Clock‘un kendi zamanlayıcısını ayarlaması ve her saniye kullanıcı arayüzünü güncellemesi işini kendi bünyesinde gerçekleştirmesi gerekiyordu.
Aşağıdaki kodu bir kere yazdığımızda, Clock‘un artık kendi kendisini güncellemesini istiyoruz:
root.render(<Clock />);Bunu yapmak için, Clock bileşenine state eklememiz gerekiyor.
State’ler, prop’larla benzerlik gösterir. Fakat sadece ilgili bileşene özeldir ve yalnızca o bileşen tarafından kontrol edilirler.
Sınıf olarak oluşturulan bileşenlerin, fonksiyon bileşenlerine göre bazı ek özelliklerinin bulunduğundan bahsetmiştik. Bahsettiğimiz ek özellik yerel state değişkenidir ve sadece sınıf bileşenlerine özgüdür.
Bir Fonksiyonun Sınıfa Dönüştürülmesi
Clock gibi bir fonksiyon bileşenini 5 adımda sınıf bileşenine dönüştürebilirsiniz:
- Öncelikle, fonksiyon ismiyle aynı isimde bir ES6 sınıfı oluşturun. Ve bu sınıfı
React.Component‘tan türetin. - Sınıfın içerisine,
render()adında boş bir fonksiyon ekleyin. - Fonksiyon bileşeni içerisindeki kodları
render()metoduna taşıyın. render()metodu içerisindekipropsyazan yerleri,this.propsile değiştirin.- Son olarak, içi boşaltılmış fonksiyonu tamamen silin.
class Clock extends React.Component {
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.props.date.toLocaleTimeString()}.</h2>
</div>
);
}
}Önceden fonksiyon bileşeni olan Clock, artık bir sınıf bileşeni haline gelmiş oldu.
Bu kodda render metodumuz, her güncelleme olduğunda yeniden çağrılacaktır. Fakat <Clock /> bileşenini aynı DOM düğümünde render ettiğimizden, Clock sınıfının yalnızca bir örneği kullanılacaktır.
Bir Sınıfa Yerel State’in Eklenmesi
date değişkenini, props’tan state’e taşımamız gerekiyor. Bunu 3 adımda gerçekleştirebiliriz:
render()metodundakithis.props.date‘ithis.state.dateile değiştirelim:
class Clock extends React.Component {
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2> </div>
);
}
}state‘in ilk kez oluşturulacağı yer olan sınıf constructor‘ını ekleyelim:
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()}; }
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}props‘ı, constructor içerisinde nasıl oluşturduğumuza yakından bakalım:
constructor(props) {
super(props); this.state = {date: new Date()};
}Sınıf bileşenleri React.Component sınıfından türetildikleri için, daima super(props)‘u çağırmaları gerekir.
<Clock />elementindendateprop’unu çıkaralım:
root.render(<Clock />);Zamanlayıcı kodunu, daha sonra Clock bileşenin içerisine ekleyeceğiz. Fakat şimdilik Clock bileşeninin son hali aşağıdaki gibi olacaktır:
class Clock extends React.Component {
constructor(props) { super(props); this.state = {date: new Date()}; }
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2> </div>
);
}
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Clock />);Şimdi Clock bileşenini, kendi zamanlayıcısını kuracak ve her saniye kendisini güncelleyecek şekilde ayarlayalım.
Bir Sınıfın Yaşam Döngüsü Kodlarının Eklenmesi
Birçok bileşene sahip uygulamalarda, bileşenler yok edildiğinde ilgili kaynakların bırakılması çok önemlidir.
Clock bileşeni ilk kez DOM’a render edildiğinde bir zamanlayıcı kurmak istiyoruz. React’te bu olaya “mounting” (değişkenin takılması) adı verilir.
Ayrıca, Clock bileşeni DOM’dan çıkarıldığında, zamanlayıcının da temizlenmesini istiyoruz. React’te bu olaya “unmounting” (değişkenin çıkarılması) adı verilir.
Clock bileşeni takılıp çıkarıldığında bazı işleri gerçekleştirebilmek için özel metotlar tanımlayabiliriz:
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
componentDidMount() { }
componentWillUnmount() { }
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}Bu metotlara “lifecycle methods” (yaşam döngüsü metotları) adı verilir.
Bileşenin çıktısı, DOM’a render edildikten sonra componentDidMount() metodu çalıştırılır. Burası aynı zamanda bir zamanlayıcı oluşturmak için en elverişli yerdir:
componentDidMount() {
this.timerID = setInterval( () => this.tick(), 1000 ); }this‘e (this.timerID) zamanlayıcı ID’sini nasıl atadığımızı inceleyebilirsiniz.
Daha önce de belirttiğimiz gibi, this.props React tarafından yönetiliyor ve this.state‘in de özel bir yaşam döngüsü var. Eğer timerID gibi veri akışına dahil olmayan değişkenleri saklamanız gerekiyorsa, bu örnekte yaptığımız gibi sınıf içerisinde değişkenler tanımlayabilirsiniz.
Oluşturduğumuz zamanlayıcıyı componentWillUnmount() yaşam döngüsü metodu içerisinde Clock bileşeninden söküp çıkaralım:
componentWillUnmount() {
clearInterval(this.timerID); }Son olarak, Clock bileşeninin saniyede bir çalıştıracağı tick() fonksiyonunu kodlayalım.
tick() fonksiyonu, this.setState()‘i çağırarak Clock bileşeninin yerel state’ini güncelleyecektir:
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);
}
tick() { this.setState({ date: new Date() }); }
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Clock />);Artık saat, her saniye başı tikleyerek mevcut zamanı görüntüleyecektir.
Şimdi kısa bir özet geçerek neler yaptığımızı ve sırasıyla hangi metotların çağrıldığını kontrol edelim:
root.render()metoduna<Clock />aktarıldığı zaman React,Clockbileşeninin constructor’ını çağırır.Clockbileşeni, mevcut saati görüntülemesi gerektiğinden,this.state‘e o anki zamanı atar. Daha sonra bu state güncellenecektir.- Devamında React,
Clockbileşenininrender()metodunu çağırır. Bu sayede React, ekranda nelerin gösterilmesi gerektiğini bilir. Sonrasında iseClock‘un render edilmiş çıktısı ile eşleşmek için ilgili DOM güncellemelerini gerçekleştirir. Clockbileşeninin çıktısı DOM’a eklendiğinde, yaşam döngüsündekicomponentDidMount()metodu çağrılır. Bu metottaClockbileşeni, her saniyede birtick()metodunun çalıştırılması gerektiğini tarayıcıya bildirir.- Tarayıcı her saniyede bir
tick()metodunu çağırır.tick()metodundaClockbileşeni, kullanıcı arayüzünü güncellemek içinsetState()metodunu çağırır ve bu metoda mevcut tarih/saat değerini aktarır.setState()‘in çağrılması sayesinde React, state’in değiştiğini anlar ve ekranda neyin görüntüleneceğini anlamak için tekrarrender()metodunu çağırır. Artıkrender()metodundakithis.state.date‘in değeri eski halinden farklı olduğundan, render çıktısı güncellenmiş zamanı içerecek demektir. Buna göre React, DOM’ı ilgili şekilde günceller. - Eğer
Clockbileşeni, DOM’dan çıkarılırsa, yaşam döngüsündekicomponentWillUnmount()metodu çağrılır ve tarayıcı tarafından zamanlayıcı durdurulmuş olur.
State’in Doğru Kullanımı
setState() hakkında bilmeniz gereken 3 şey bulunmaktadır.
State’i Doğrudan Değiştirmeyiniz
Örneğin, aşağıdaki kod bileşeni yeniden render etmeyecektir:
// Yanlış kullanım
this.state.comment = 'Hello';Bunun yerine setState() kullanınız:
// Doğru kullanım
this.setState({comment: 'Hello'});this.state‘e atama yapmanız gereken tek yer, ilgili bileşenin constructor’ıdır.
State Güncellemeleri Asenkron Olabilir
React, çoklu setState() çağrılarını, performans için tekil bir güncellemeye dönüştürebilir.
this.props ve this.state, asenkron olarak güncellenebildiklerinden, sonraki state’i hesaplarken bu nesnelerin mevcut değerlerine güvenmemelisiniz.
Örneğin, aşağıdaki kod counter‘ı güncellemeyebilir:
// Yanlış kullanım
this.setState({
counter: this.state.counter + this.props.increment,
});Bunu düzeltmek için, setState()‘in ikinci formunu kullanmamız gerekir. Bu formda setState() fonksiyonu, parametre olarak nesne yerine fonksiyon alır. Bu fonksiyon, ilk parametre olarak önceki state’i, ikinci parametre olarak da o anda güncellenen props değerini alır:
// Doğru kullanım
this.setState((state, props) => ({
counter: state.counter + props.increment
}));Yukarıda bir ok fonksiyonu kullandık. Fakat normal fonksiyonlarla da gayet çalışabilir:
// Doğru kullanım
this.setState(function(state, props) {
return {
counter: state.counter + props.increment
};
});State Güncellemeleri Birleştirilir
React, setState()‘i çağırdığınızda, parametre olarak verdiğiniz nesneyi alıp mevcut state’e aktarır.
Örneğin, state’iniz aşağıdaki gibi birçok bağımsız değişkeni içerebilir:
constructor(props) {
super(props);
this.state = {
posts: [], comments: [] };
}Ve siz de bu değişkenleri, ayrı birer setState() çağrıları ile güncellemek isteyebilirsiniz:
componentDidMount() {
fetchPosts().then(response => {
this.setState({
posts: response.posts });
});
fetchComments().then(response => {
this.setState({
comments: response.comments });
});
}Birleşme işlemi yüzeysel olduğundan, this.setState({comments}) çağrısı this.state.posts değişkenini değişmeden bırakırken, this.state.comments‘i tamamıyla değiştirecektir.
Verinin Alt Bileşenlere Aktarılması
Ne üst ne de alt bileşenler, belirli bir bileşenin state’li veya state’siz olduğunu bilebilir. Ayrıca o bileşenin fonksiyon veya sınıf olarak tanımlanmasını da önemsemezler.
Bu nedenle state’e, yerel state denir. State, kendisine sahip olan ve kendisini ayarlayan bileşen haricinde hiçbir bileşen için erişilebilir değildir.
Bir bileşen kendi state’ini, prop’lar aracılığıyla alt bileşenlere aktarabilir:
<FormattedDate date={this.state.date} />FormattedDate bileşeni, date değişkenini props’tan alabilir ve bunu alırken Clock‘un state’inden mi yoksa prop’undan mı geldiğini bilemez. Hatta date değişkeni, Clock bileşeni içerisinde state’ten harici olarak tanımlanmış bir değer de olabilir ve bunu bilmesi mümkün değildir:
function FormattedDate(props) {
return <h2>It is {props.date.toLocaleTimeString()}.</h2>;
}Bu olaya genellikle yukarıdan-aşağıya veya tek yönlü veri akışı denir. Her state, belirli bir bileşen tarafından tutulur. Bu bileşenden türetilen herhangi bir veri veya kullanıcı arayüzü, yalnızca bu bileşenin altındaki bileşen ağacına etki edebilir.
Bileşen ağacını, prop’lardan oluşan bir şelale olarak düşünebilirsiniz. Her bileşenin state’i, prop’ları istenilen bir noktada birleştirebilen ve aynı zamanda alt bileşenlere de akıtan ek bir su kaynağı gibidir.
Tüm bileşenlerin tamamen izole olduğunu göstermek için, 3 adet <Clock> render eden bir App bileşeni oluşturabiliriz:
function App() {
return (
<div>
<Clock /> <Clock /> <Clock /> </div>
);
}Bu örnekte yer alan her bir Clock bileşeni, kendi zamanlayıcısını oluşturup birbirinden bağımsız bir şekilde güncellemektedir.
React uygulamalarında, bir bileşenin state’li veya state’siz olması, bir kodlama detayıdır ve zaman içerisinde değişkenlik gösterebilir. State’li bileşenler içerisinde, state’siz bileşenleri kullanabilirsiniz veya bu durumun tam tersi de geçerlidir.