React Native initialRoute’u state’e göre belirleme.

Makaleyi yazma sebebim şu linkte Üstün Özgür’ün bahsettiği bir bug’a benimde şans eseri takılmam oldu. Konu hakkında araştırma yaptıktan sonra emin olmamakla birlikte tam bir bug olmadığının kanaatindeyim.

Bug’ı Üstün hocam kod ile açıklamış bende kısaca neden ve nasıl ilişkisiyle yazılı olarak açıklıyacağım.

Kısaca uygulama açıldığında api tarafından token alınır, sonraki her kullanıcı request’i bu token ile gerçekleştirilir. Token aldıktan veya alamadıktan sonra kullanıcının hangi sayfaya düşeceğini NavigatorIOS component’ine initialRoute propu olarak geçilir. Çok güzel yazması :)

Sözlü olarak anlattığım kısım :

constructor(props) {
super(props);
this.state = {
token: undefined
}
}
componentDidMount(){
Core.getToken(function(error, data){
if (!error) {
this.setState({ token : data });
} else {
this.setState({ token : false });
}
}.bind(this));
}
render() {
return (
<NavigatorIOS ref="navigator"
style={styles.nav}
initialRoute={{
title: "blabla",
component: this.state.token != undefined ? TabPage : Login
}} />
);
}

Aslında kodu okuduğumuzda hiç bir sorun ile karşılaşmadan TabPage veya Login component’inin monte edilmesi gerekir .

Component yaşam döngüsünde componentDidMount methodu hakkıda render methodunun 1 kez çalıştırılacağı taahhüt ediliyor. Render methodunun ilk çalışmasında state durumuna göre Tabpage veya Login component’i ilk rota olarak belirlenmesi gerekiyor.Token aldığımız fonksiyon asenkron olduğu için ilk durumda doğal olarak Login component’i monte edilir (token : undefined). State Tabpage component’inin monte edilmesi için güncellendiğinde ikinci kez render methodu çağrılır (token : Object), Tabpage yerine tekrar Login Component’i karşımıza çıkar işte bug dediğimiz şeyde bu. State değişti ama görüntü değişmedi (gerçekten sinir bozucu) bunun sebebi Navigator veya NavigatorIOS componentleri birer sahne hazırlıyor olması ve state değişkenlerinin sahneleri değiştiremiyor olmasıdır. Bu sahneleri değiştirmek için componentin replace methodu hali hazırda var ama asenkron gelen bir veri için kullanıcıya login olmadan uygulamanın içini gösteremeyiz .

kimse giriş yapmadan uygulamayı kullanamaz !!

NavigatorIOS componenti aslında IOS dünyasındaki UINavigationController’ın kendisidir. NavigatorIOS bütün rota mekanizmasını yönetiyor sahneler arasında geçişler ve diğer işlemler state ile değil component’in kendi replace, pop, push methodlarıyla hallediliyor . Tabi bu methodlara state geçebiliyoruz . Buna göre istediğimiz şeyin olması için render methodunun sadece bir kez çalışması lazım bunun içinde componentWillMount kullanmamız gerekir o zamanda state değiştiremeyiz çünkü ortalıkta state’de yok *1.

Elimizdekiler :

  • State ile sahne değiştiremiyoruz
  • Render methodunun önüne geçemiyoruz (bu durumda)
  • Render iki kez kesinlikle çalışıyor .
  • Kullanıcıya ait olduğu sayfayı göstermemiz gerekiyor

Çözüm :

constructor(props) {
super(props);
this.state = {
token: undefined,
load:true,
}
}
componentDidMount(){
Core.getToken(function(error, data){
if (!error) {
this.setState({ token : token , load:false });
} else {
this.setState({ token : false ,load:false});
}
}.bind(this));
}
render() {
if (this.state.load) {
return <View><Text>loading...</Text></View>
}
return (
<NavigatorIOS ref="navigator"
style={styles.nav}
initialRoute={{
title: "blabla",
component: this.state.token != undefined && this.state.token != false ? TabPage : Login,
}} />
);
}

Bu durumda sadece asenkron fonksiyonu beklemek gerekiyor, state değişkenlerimiz arasına birde load adında bir değişken daha koyuyoruz bu bize asenkron fonksiyondan cevap gelene kadar kullanıcıya bilgi vermemizi sağlayacaktır daha sonra ise ikinci bir koşul koyarak token’ın undefined ve false olmaması durumunda içeri alıyoruz . Aksi durumda da login .

Kısaca çözümün işleyiş biçimi şu şekilde : Değişen bir state’in bize lazım olan kısmı en son halidir (bu durumda tabi ), bu hali alana kadar bizim render methodunu tabiri caizse geçiştirmemiz gerekiyor bunun içinde ikinci bir state tutuyoruz ki bu state token state’nin en son halini aldığında artık görevini tamamlamış olacak (load:false). Bu sayede render methodu ne kadar çalışırsa çalışsın biz NavigatorIOS componentine state’e bağlı ama bir yandan bağımsız yani değişmeyecek bir component vermiş oluyoruz .

*1 — Stephen Hawking’in belgeselinden esinlendiğim söz : “Durun bir dakika burada ışık olamaz çünkü ışığın kendisi de yok “

Happy coding !