Merhaba. Bu sayfada https://reactnavigation.org/ sitesinde yayınlanan React Navigation 5.x versiyonunun dokümantasyonunun başlangıç kısmını lisanım yettiğince Türkçeleştireceğim. Bu sayfayı okurken şunları bilmelisiniz:

Navigation kelimesini Türkçeye gezgin olarak çevrimek istemedim. Navigation veya navigator kelimeleri sayfalar arasında gezmek, sayfayı yönlendirmek... cümle içine nasıl denk geliyorsa öyle anlamlara geliyor ve sanırım gezgin deyince konsepti anlamak zorlaşıyor.

Component kelimesini de bileşen olarak çevirmeyi düşünmüyorum. React Native kullanıyorsak bu kelimeye bu şekilde aşinayızdır. Aynı şekilde state kelimesini de durum diye çevirmeyeceğim. Ben yine de bir kelimenin hangi anlamlarda kullanıldığını daha anlaşılır kılmak için anlamları/manaları bu şekilde / ile ayırarak gösterdim.

Benim yazının arasına girip kendimce eklediğim yazılar olursa bunları bu renkte belirteceğim.

İsterseniz asıl dokümantasyonu da burayla eş zamanlı takip edebilesiniz diye sayfayı CSS ile az-çok aslına benzettim. Ama dayanamayıp kendi yorumumu kattığım stillendirmeler de yok değil. :)


Versiyon: 5.x

Başlarken

Burada anlatılan bilgilerle tipik, küçük bir mobil uygulama yapmanıza yetecek bilgiler edinecek ve daha derine inmek için temel atmış olacaksınız.

Ön Gereksinimler

Eğer zaten Javascript'i biliyorsanız React Navigation kullanmaya hızlıca başlayabilirsiniz ama bilmiyorsanız önce orada kendinizi geliştirmelisiniz.

Javascript'te sıkıntınız yoksa burada size yardımcı olacak bazı kaynaklar var:

  1. React Native Express (1-4 arası bölümler)
  2. Main Concepts of React
  3. React Hooks
  4. React Context (Gelişmiş)

Kurulum

React Native için gerekli paketleri kurun:

npm ile kurulum için:

npm install @react-navigation/native

yarn ile kurulum için:

yarn add @react-navigation/native

React Navigation bazı temel araçlardan oluşur. Bu araçlar, navigator yapısını kurmak için kullanılır. Merak etmeyin, ileride neden bahsettiğimizi anlayacaksınız. Biz şimdilik sonradan uğraşmamak temel bağımlılıkların kurulum işini halledelim ve hızlıca kod yazmaya geçelim.

Şimdi kuracağımız kütüphaneler react-native-gesture-handler, react-native-reanimated, react-native-screens, react-native-safe-area-context ve @react-native-community/masked-view. React Navigation, çalışmak için bu kütüphanelere bağımlı. Sizde bu kütüphanelerin son sürümleri zaten kuruluysa yeniden kurmanıza gerek yok tabii ki.

Expo projeniz için bağımlılıkların kurulması

Projenizin kök klasörüne gidin ve şu komutu verin:

expo install react-native-gesture-handler react-native-reanimated react-native-screens react-native-safe-area-context @react-native-community/masked-view

Böylece Merhaba React Navigation başlığına geçebilirsiniz.

Yalın React Native projeniz için bağımlılıkların kurulması

Projenizin kök klasörüne gidin ve şu komutu verin:

npm ile kurulum için:

npm install react-native-reanimated react-native-gesture-handler react-native-screens react-native-safe-area-context @react-native-community/masked-view

yarn ile kurulum için:

yarn add react-native-reanimated react-native-gesture-handler react-native-screens react-native-safe-area-context @react-native-community/masked-view
Not: Kurulumdan sonra eş bağımlılıklarla ilgili uyarılar alabilirsiniz. Bunlar genelde bazı paketlerde belirtilen yanlış versiyon aralıklarından kaynaklanır. Uygulamanız oluşturulabiliyorsa bu uyarıları göz ardı edebilirsiniz.
Not: Eğer projenizde react-native-windows kullanıyorsanız react-native-gesture-handler da kullanın.

React Native 0.60 ve üzeri sürümler için linkleme işlemi otomatik yapılır. Yani sizin run react-native link yazmanıza gerek yok.

Eğer Mac'te çalışıyor ve iOS için geliştirme yapıyorsanız linkleme yapmak için pods'u (Cocoapods) kurmanız gerekiyor.

npx pod-install ios

react-native-gesture-handler kurulumunu tamamlamak için index.js veya App.js gibi uygulamanızın giriş dosyasının en üst satırına (bu satırdan önce başka bir satır olmadığına dikkat edin) ekleyin:

import 'react-native-gesture-handler';
Not: Eğer Android veya iOS için geliştirme yapıyorsanız bu adımı atlamayın. Geliştirme sırasında düzgün çalışsa bile yayınlama aşamasında uygulamanız çökebilir. Diğer platformlar için bu adımı atlayabilirsiniz.

Şimdi tüm uygulamayı NavigationContainer ile sarmamız gerekiyor. Genellikle giriş dosyanız olan index.js veya App.js içinde bunu yaparız.

import 'react-native-gesture-handler';
import * as React from 'react';
import { NavigationContainer } from '@react-navigation/native';

export default function App() {
  return (
    <NavigationContainer>{/* Rest of your app code */}</NavigationContainer>
  );
}
Not: Bir navigator kullandığınızda (stack navigator gibi) onun bağımlılıklarını eklemek için kurulum yönergelerini de uygulamanız gerekir. Eğer "Unable to resolve module" diye bir hata alıyorsanız, bu modülü projenize kurmanız gerekiyor demektir.

Artık uygulamanızı cihaz/simülatör üzerinde build etmeye hazırsınız.


Merhaba React Navigation

Bir web tarayıcıda, başka bir sayfaya gitmek için <a> tag'ını kullanırsınız. Kullanıcı link'e tıkladığında URL, tarayıcının history stack'ine (geçmiş yığınına) eklenir. Kullanıcı geri butonuna bastığında tarayıcı, yığına son eklenen (yığının en üstündeki) URL'i yığından alıp gösterir ve böylece aktif sayfa önceki sayfa olur. React Native, yerleşik olarak, tarayıcılardaki gibi bir history stack'e sahip değil. İşte bu noktada da React Navigation devreye giriyor.

React Navigation'un Stack Navigator sağlayıcısı (stack navigator provider) ekranlar arasında geçişi ve navigation geçmişi yönetimini yapmanın bir yoludur. Eğer sizin uygulamanız tek bir yığın/stack navigator'e sahipse uygulamanız web sayfası gibi çalışır. Kullanıcılar sayfa değiştirmeye kalktıklarında yığına/stack'a sayfalar eklenir/çıkarılır ve kullanıcıya yığının üstündeki ekran sunulur. Bunun bir web tarayıcıyla arasındaki fark, sayfa geçişleri animasyonlarıdır. Yığın sağlayıcı, Android ve iOS kullanıcılarının kendi sistemlerinde alışık oldukları sayfa geçişini (iOS için yatay, Android için dikey sayfa geçişleri gibi) sağlamış olur.

Birazdan en yaygın kullanılan navigation aracı olan yığın/stack navigator'u oluşturmakla işe başlayacağız: createStackNavigator

Yığın kelimesi burada anlatılanı güzel anlatıyor. Üst üste duran kartlar gibi düşünün. Siz AnaSayfa adlı sayfadasınız. Sistem, masanın üstündeki yığın alanına üzerinde Anasayfa yazılı bir kart koyuyor. Hakkımızda sayfasına geçtiğinizde AnaSayfa yazılı bir kartın üzerine Hakkımızda kartını konuyor. İletişim sayfasına geçtiğinizde de Hakkımızda kartının üstüne İletişim yazılı kart konuyor. Artık yığında 3 tane kart/sayfa var. Geri butonuna basıyorsunuz ve sistem en üstteki kartı kaldırıp atıyor ve şimdi en üstte Hakkımızda sayfası olduğu için onu görüyorsunuz. Bu sefer de Ürünler sayfasına gittiniz. Sistem, Hakkımızda kartının üstüne Ürünler kartını koyuyor. Ürünler sayfasındayken İletişim sayfasına geçtiğinizde de Ürünler kartının üstüne İletişim kartı konuyor. Şimdi yığında sırasıyla Anasayfa>Hakkımızda>Ürünler>İletişim kartları/sayfaları var ve en üstte İletişim kartı olduğu için onu görüyoruz. Peki Hakkımızda sayfasına gitmek istediğimizde ne oluyor? Sistem yığını kontrol ediyor. Hakkımızda sayfası yığında var mı? Var. O zaman sistem, Hakkımızda sayfası en üstte kalana kadar yığının üstündeki kartları atıyor. Şimdi yığında Anasayfa>Hakkımızda var. Yani geri butonuna basarsak Hakkımızda kartı da atılır ve AnaSayfa'ya döneriz. Yığınımız "son giren ilk çıkar" mantığıyla çalışıyor.

Stack Navigator kütüphanesinin kurulumu

React Navigation'da sayfalar arası gezinme işini sağlayan her aracın kendi kütüphanesi vardır. Stack navigator (yığın navigator'u) kullanmak istiyorsak şu kütüphaneyi kurmamız gerekir: @react-navigation/stack:

npm ile kurulum için:

npm install @react-navigation/stack

yarn ile kurulum için:

yarn add @react-navigation/stack
💡 @react-navigation/stack çalışmak için @react-native-community/masked-view ve kurulum aşamasındaki diğer kütüphanelere bağımlıdır. Eğer bu kurulumları henüz yapmadıysanız geri dönüp bu kurulumları nasıl yapacağınızı okuyun.

Yığın/stack navigator'u oluşturmak

createStackNavigator, şu 2 property'i barındıran bir obje döndüren bir fonksiyondur: Screen ve Navigator. Bunların ikisi de navigation ayarların yapmak için kullanacağınız React component'leridir. Yakında göreceğiniz üzere Navigator, yönlendirme ayarlarını içeren Screen component'leri barındırmalıdır.

NavigationContainer, navigation ağacımızı yöneten ve navigation state'ini tutan bir component'tir. Bu component, tüm navigation yapısını sarmalıdır. Genellikle bu komponent bütün uygulamanızı sararak, App.js'ten (veya sizin uygulamanızın giriş dosyası neyse neyse) render edilmesi için export edilir.

// Yeni bir uygulama oluşturduğunuzda kök klasörde gelen App.js

import * as React from 'react';
import { View, Text } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';

function HomeScreen() {
    return (
        <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
        <Text>Home Screen</Text>
        </View>
    );
}

const Stack = createStackNavigator();

function App() {
    return (
        <NavigationContainer>
            <Stack.Navigator>
                <Stack.Screen name="Home" component={HomeScreen} />
            </Stack.Navigator>
        </NavigationContainer>
    );
}

export default App;

Bu örneği Snack üzerinde deneyin

React Navigation 5.x İlk Örnek için Ekran Görüntüsü

Kodu çalıştırırsanız üstteki görsel gibi bir sayfa göreceksiniz. Navigation barında ve içerikte gördüğünüz stil, stack navigator için varsayılan ayarlara göre verildi. Bunları nasıl değiştirebileceğimizi ileride göreceğiz.

name="Home" yazan yerdeki Home değerini, yani yönlendirme adını ne verdiğiniz önemli değildir. "Home", "home", "HOME", "AnaSayfa" gibi istediğiniz değeri verebilirsiniz. Biz ilk harfi büyük şekilde yönlendirme adı vermenizi öneririz. Bu, "şu sayfaya git" derken belirteceğiniz addır.
Bir Screen component'i için name ve component property'leri zorunludur. Kullanabileceğiniz diğer ayarlar için stack navigator reference sayfasını inceleyebilirsiniz.

Navigator'u ayarlamak

Tüm yönlendirme/routing ayarları, Navigator'umuzda varsayılan olarak verilmiştir. Biz Navigator'umuza varsayılan bir sayfa/ekran/rota atamadık. Bu yüzden varsayılan ayarlarla HomeScreen sayfasını görüyorsunuz.

Stack/yığın navigator'umuzda bir sayfa/ekran/rota daha ekleyelim ve HomeScreen ekranımızı da ilk açılış ekranı olacak şekilde ayarlayalım.

Ben sayfanın ilk halinin üzerine kodları ekledim ve yeni ekleyeceğiniz kodları çerçeve içine aldım. Böyle daha anlaşılır olur.
// Yeni bir uygulama oluşturduğunuzda kök klasörde gelen App.js

import * as React from 'react';
import { View, Text } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';

function HomeScreen() {
    return (
        <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
            <Text>Home Screen</Text>
        </View>
    );
}

function DetailsScreen() {
    return (
        <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
            <Text>Details Screen</Text>
        </View>
    );
}

const Stack = createStackNavigator();
    
function App() {
    return (
        <NavigationContainer>
            <Stack.Navigator initialRouteName="Home">
                <Stack.Screen name="Home" component={HomeScreen} />
                <Stack.Screen name="Details" component={DetailsScreen} />
            </Stack.Navigator>
        </NavigationContainer>
    );
}

Bu örneği Snack üzerinde deneyin

Artık navigator'umuzun iki ekranı var: bir Home ekranı ve bir de Details ekranı. Screen component'i, gezinmek için kullanacağımız sayfa/ekran/rota adına karşılık gelen bir name prop'u ve render edeceği component'i belirten bir component prop'u alır.

Bu kodda ilk açılış ekranı, Home adıyla ifade ettiğimiz HomeScreen component'idir. Details yolu da DetailsScreen component'ine karşılık gelir. Stack için ilk yol (yığının en üstündeki yol) Home yoludur. Bunu değiştirip kaydedin ve uygulamayı reload edin (çünkü React Native'in Fast Refresh'i initialRouteName'deki değişikliği güncellemeyecektir) ve ilk ekran olarak Details ekranının geldiğini cihazınızda/simülatörünüzde görün. Sonra Home ekranına geri dönün ve yine reload edin.

Not: component prop'u component değeri alır. Değer olarak fonksiyon vermeyin (Örn: component={()=><DetailsScreen />}). Aksi taktirde component'iniz üst component yeniden oluşturulduğunda tüm state'ini kaybederek bağlantısını kesip yeniden bağlanacaktır.

Ayarları özelleştirmek

Navigator'un her bir screen'inde/ekranında bazı özelleştirmeler yapılabilir. Mesela ekranın header kısmında oluşan başlık... Bu seçenekler, her bir Screen component'i için options parametresiyle verilebilir.

<Stack.Screen name="Home" component={HomeScreen} options={{ title: 'Overview' }} />

Ekrana prop göndermek

Bazen bir sayfaya/ekrana ek parametreler aktarmamız gerekebilir. Bunu 2 yolla yapmamız mümkün:

  1. React context kullanarak verileri iletmek için navigator'u bir context provider içine alın. (önerilir)
  2. Screen için bir component prop'u belirtmek yerine ekran için bir render callback kullanın:
    <Stack.Screen name="Home">
        {props => <HomeScreen {...props} extraData={someData} />}
    </Stack.Screen>
Varsayılan olarak React Navigation, gereksiz işlemeleri önlemek için, screen bileşenine optimizasyonlar uygular. Ama siz render callback yöntemini kullanırsanız bu optimizasyonlar ortadan kalkar. Dolayısıyla, bir render callback kullanıyorsanız, performans sorunlarını önlemek için screen component'iniz için React.memo veya React.PureComponent kullandığınızdan emin olmanız gerekir.

Sırada ne var?

Doğal olarak akıllardaki soru: "Home ekranından Details ekranına nasıl yönlendirme yapacağım?". Sonraki konu bunu içeriyor.


Ekranlar Arasında Geçiş

Önceki bölüm Merhaba React Navigation ile iki rotalı/sayfalı/ekranlı stack navigator yaptık (Home ve Details), ama kullanıcının Home ekranından Details ekranına nasıl geçeceğini öğrenmedik.

Eğer bu işi bir web tarayıcıda yapmaya kalksaydık şu şekilde bir kodu rahatça html'e verebilirdik:

<a href="details.html">Go to Details</a>

Diğer bir yol da şu olabilirdi:

<a onClick={() => { window.location.href='details.html' }}>Go to Details</a>

Biz ikincisine benzer bi'şey yapacağız. Ama window.location yerine ekran/sayfa component'lerimize aktarılan (component'ten prop olarak alabileceğimiz) navigation prop'unu kullanacağız.

Yeni bir ekrana geçmek

Ben yine sayfanın ilk halinin üzerine kodları ekledim ve yeni ekleyeceğiniz kodları çerçeve içine aldım.
// Yeni bir uygulama oluşturduğunuzda kök klasörde gelen App.js

import * as React from 'react';
import { Button, View, Text } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';

function HomeScreen( {navigation} ) {
    return (
        <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
            <Text>Home Screen</Text>
            <Button title="Go to Details" onPress={()=>navigation.navigate('Details')}
        </View>
    );
}

function DetailsScreen() {
    return (
        <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
            <Text>Details Screen</Text>
        </View>
    );
}

const Stack = createStackNavigator();
    
function App() {
    return (
        <NavigationContainer>
            <Stack.Navigator initialRouteName="Home">
                <Stack.Screen name="Home" component={HomeScreen} />
                <Stack.Screen name="Details" component={DetailsScreen} />
            </Stack.Navigator>
        </NavigationContainer>
    );
}

Bu örneği Snack üzerinde deneyin

Eklediğimiz kodları parça parça inceleyelim:

Navigator'umuzta tanımlamadığımız bir rota adını navigation.navigate() fonksiyonuna verirsek; geliştirme aşamasındayken bir hata yazdırır, yayınlama aşamasındayken hiçbir şey olmaz. Yani sadece navigator'umuzda tanımlanmış rotalara gidebiliriz. Rastgele bir component'e gidemeyiz.

Şimdi iki rotaya/ekrana/sayfaya sahip bir stack'ımız/yığınımız var. Peki Details rotasından/ekranından yine Details rotasına/ekranına gidersek ne olur?

Bir ekrana birkaç kez geçmek

// Yeni bir uygulama oluşturduğunuzda kök klasörde gelen App.js

import * as React from 'react';
import { Button, View, Text } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';

function HomeScreen({navigation}) {
    return (
        <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
            <Text>Home Screen</Text>
            <Button title="Go to Details" onPress={()=>navigation.navigate('Details')}
        </View>
    );
}

function DetailsScreen( {navigation} ) {
    return (
        <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
            <Text>Details Screen</Text>
            <Button title="Go to Details... again" onPress={()=>navigation.navigate('Details')}
        </View>
    );
}

const Stack = createStackNavigator();
    
function App() {
    return (
        <NavigationContainer>
            <Stack.Navigator initialRouteName="Home">
                <Stack.Screen name="Home" component={HomeScreen} />
                <Stack.Screen name="Details" component={DetailsScreen} />
            </Stack.Navigator>
        </NavigationContainer>
    );
}

Bu örneği Snack üzerinde deneyin

Bu kodu çalıştırıp "Go to Details... again" butonuna dokunursanız hiçbir şey yapmadığını fark edeceksiniz! Çünkü zaten Details ekranındayız. navigate() fonksiyonuna bir rota verdiğimizde kabaca ona "bu sayfaya git" deriz. Ama zaten o sayfadaysak hiçbir şey yapmaz.

E hadi başka bir Details ekranı eklemek istediğinizi varsayalım. Bu, bir ekrana benzersiz veriler aktardığınız durumlarda oldukça yaygındır (ileride params konusuna geleceğiz!). Bunu yapmak için navigate() fonksiyonu yerine push() fonksiyonuyla değiştirin. Bu, mevcut ekran geçmişinizden bağımsız olarak başka bir ekran eklemek istediğimizi belirtir. Aynı sayfanın yığının üstüne tekrar atıldığını, üst üste bindiğini düşünebiliriz.

Bu sefer sayfadaki bütün kodları eklemiyorum. Sadece DetailsScreen component'indeki Button'u aşağıdaki gibi olacak.
<Button title="Go to Details... again" onPress={() => navigation.push('Details')} />

Bu örneği Snack üzerinde deneyin

push() fonksiyonunu her çağırdığımızda navigation yığınına/stack'ına yeni bir rota/ekran ekleriz. navigate() fonksiyonunu çağırdığımızdaysa önce mevcut rotayı bulmaya çalışır ve yalnızca, eğer yığında/stack'ta henüz bir tane yoksa, yeni rotayı yığına ekler.

Geri gelmek

Stack navigator tarafından otomatik sağlanan header, o an açık olan ekrandan geri dönmek mümkün olduğunda otomatik olarak geri dönmeye yarayan bir buton içerir (O anda yığında sadece 1 tane ekran/rota/sayfa varsa geri dönülecek bir ekran/rota/sayfa olmadığından geri düğmesi de yoktur).

Bazen bu davranışı otomatik atanan geri butonuyla değil de programatik olarak tetiklemek isteyebilirsiniz. Bunun için, navigate() ve push() fonksiyonlarınızı kullandığınız aynı yöntemle navigation.goBack() fonksiyonunu kullanabilirsiniz. DetailsScreen component'ine bir buton daha ekleyip bunu test edebilirsiniz:

// Yeni bir uygulama oluşturduğunuzda kök klasörde gelen App.js

import * as React from 'react';
import { Button, View, Text } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';

function HomeScreen({navigation}) {
    return (
        <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
            <Text>Home Screen</Text>
            <Button title="Go to Details" onPress={()=>navigation.navigate('Details')}
        </View>
    );
}

function DetailsScreen({navigation}) {
    return (
        <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
            <Text>Details Screen</Text>
            <Button title="Go to Details... again" onPress={()=>navigation.push('Details')}
            <Button title="Go to Home" onPress={()=>navigation.navigate('Home')}
            <Button title="Go back" onPress={()=>navigation.goBack()}
        </View>
    );
}

const Stack = createStackNavigator();
    
function App() {
    return (
        <NavigationContainer>
            <Stack.Navigator initialRouteName="Home">
                <Stack.Screen name="Home" component={HomeScreen} />
                <Stack.Screen name="Details" component={DetailsScreen} />
            </Stack.Navigator>
        </NavigationContainer>
    );
}

Bu örneği Snack üzerinde deneyin

Android'de React Navigation, donanımın sağladığı geri düğmesine bağlanıp kullanıcı bu düğmeye bastığında goBack() fonksiyonunu çalıştırır. Böylece donanımın geri butonu da kullanıcının beklediği gibi davranmış olur.

Bazen de yığının derinliklerindeyken ana ekrana dönmek ve yığındaki diğer sayfaları yok saymak isteyebilirsiniz. Bu durumda, Home ekranına geri dönmek istediğinizde pekala navigation.navigate('Home') özelliğini kullanabilirsiniz (push kullanmayın. navigate kullanın ve farkı görün). Buna alternatif olarak navigation.popToTop() kullanabilirsiniz. Bu da sizi yığındaki ilk sayfaya döndürür.

// Yeni bir uygulama oluşturduğunuzda kök klasörde gelen App.js

import * as React from 'react';
import { Button, View, Text } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';

function HomeScreen({navigation}) {
    return (
        <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
            <Text>Home Screen</Text>
            <Button title="Go to Details" onPress={()=>navigation.navigate('Details')}
        </View>
    );
}

function DetailsScreen({navigation}) {
    return (
        <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
            <Text>Details Screen</Text>
            <Button title="Go to Details... again" onPress={()=>navigation.push('Details')}
            <Button title="Go to Home" onPress={()=>navigation.navigate('Home')}
            <Button title="Go back" onPress={()=>navigation.goBack()}
            <Button title="Go back to first screen in stack" onPress={()=>navigation.popToTop()}
        </View>
    );
}

const Stack = createStackNavigator();
    
function App() {
    return (
        <NavigationContainer>
            <Stack.Navigator initialRouteName="Home">
                <Stack.Screen name="Home" component={HomeScreen} />
                <Stack.Screen name="Details" component={DetailsScreen} />
            </Stack.Navigator>
        </NavigationContainer>
    );
}

Bu örneği Snack üzerinde deneyin


Parametreleri sayfalara aktarmak

Daha önce parametreler hakkında daha detaylı bilgi vereceğiz demiştik. Pekala, şimdi zamanı geldi!

Artık bir stack navigator'unu nasıl oluşturacağımızı ve bununla ekranlar arasında nasıl geçiş yapabileceğimizi bildiğimize göre, ekranlar arasında geçerken nasıl veri aktarabileceğimize bakalım.

Veri aktarımını iki taraflı (veriyi gönderen ve alan) olarak ele alırsak

  1. navigation.navigate() fonksiyonunun 2.parametresi olarak verileri gönderebiliriz.
    navigation.navigate('Details', { /* parametreler buradan gönderilebilir */ })
  2. Screen component'imizde route.params ile parametreleri alabiliriz.
Geçtiğiniz parametrelerin JSON.stringify() edilebilir öneriyoruz.

HomeScreen component'imizden/sayfamızdan/ekranımızdan veri gönderip DetailsScreen içinde yakalayalım.

// Yeni bir uygulama oluşturduğunuzda kök klasörde gelen App.js

import * as React from 'react';
import { Button, View, Text } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';

function HomeScreen({navigation}) {
    return (
        <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
            <Text>Home Screen</Text>
            <Button title="Go to Details" onPress={()=>navigation.navigate('Details', 
                {
    itemId: 86,
    otherParam: 'ne istersek...'
}
            )}
        </View>
    );
}

function DetailsScreen( { route, navigation } ) {

    const { itemId, otherParam } = route.params;
    
    return (
        <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
            <Text>Details Screen</Text>
            <Text>ItemId: {JSON.stringify(itemId)}</Text>
<Text>otherParam: {JSON.stringify(otherParam)}</Text>
            <Button title="Go to Details... again" onPress={()=>navigation.push('Details', 
                {
    itemId: Math.floor(Math.random() * 100),
}
            )}
            <Button title="Go to Home" onPress={()=>navigation.navigate('Home')}
            <Button title="Go back" onPress={()=>navigation.goBack()}
            <Button title="Go back to first screen in stack" onPress={()=>navigation.popToTop()}
        </View>
    );
}

const Stack = createStackNavigator();
    
function App() {
    return (
        <NavigationContainer>
            <Stack.Navigator initialRouteName="Home">
                <Stack.Screen name="Home" component={HomeScreen} />
                <Stack.Screen name="Details" component={DetailsScreen} />
            </Stack.Navigator>
        </NavigationContainer>
    );
}

Bu örneği Snack üzerinde deneyin

React Navigation 5.x İlk Örnek için Ekran Görüntüsü

Parametreleri güncellemek

Ekranlar/screen'ler/sayfa component'leri, tıpkı state'lerini güncelledikleri gibi, parametrelerini de güncelleyebilirler. navigation.setParams() fonksiyonu, bir ekranın parametrelerini güncellemenizi sağlar. Daha fazla ayrıntı için API reference for setParams bölümünü inceleyin.

Basit kullanım:

navigation.setParams( { query: 'someText', } )
Not: setParams fonksiyonunu, options ile ayarlanan başlık vb. gibi ekran özelliklerini güncellemek için kullanmaktan kaçının. Bunları güncelleyecekseniz setOptions() kullanın.

Parametrelerin ilk durumları (Initial params)

Ekrana başlangıç değerleri olan parametreler gönderebilirsiniz. Siz ekrana giderken herhangi bir parametre belirtmezseniz bu parametreler bu ilk değerleriyle sayfada kullanılabilecektir. Daha önce sayfaya gönderdiğimiz parametreleri nasıl okuyorduysak bunları da aynı şekilde okuyabiliriz. Bunu sağlayan, Screen component'inin initialParams prop'udur.

<Stack.Screen name="Details" component={DetailsScreen} initialParams={{ itemId: 42 }} />

Önceki ekrana parametre geçmek

Parametreleri sadece yeni ekrana geçerken mi verebiliyoruz, diye düşünebilirsiniz. Aslında önceki ekrana geçerken de parametre göndermemiz gerekebilir. Mesela "Create Post"/"Gönderi Oluştur" butonu olan bir ekranınız olsun ve bu butona basıldığında yeni bir ekran açılsın. Gönderi oluşturulduktan önceki ekrana geri dönelim ve gönderi verilerini de önceki ekrana taşıyalım.

Şimdi siz burada navigation.goBack() fonksiyonuna parametre ekleyeceğiz sanıyorsunuz ama öyle değil. Yine navigation.navigate() fonksiyonunu kullanacaksınız ve route olarak önceki sayfanın adını verip parametre göndereceksiniz. Aslında yukarıda yaptığımız sayfaya geçerken parametre göndermekle aynı işlemi yapıyoruz. Aşağıdaki örnekte CreatePostScreen component'ine gidiliyor ve metin kutusuna girilen veri, butona basılınca HomeScreen component'ine aktarılıyor. Ayrıca, (dokümantasyondan farklı olarak) tüm sayfanın kodlarını verip yapılacak değişiklikleri işaretleyeceğim. Aynı örnek üzerinde ilerlersek daha iyi anlaşılır sanırım.

// Yeni bir uygulama oluşturduğunuzda kök klasörde gelen App.js

import * as React from 'react';
import { Button, View, Text } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';

function HomeScreen({ navigation , route }) {

    React.useEffect(() => {
    if (route.params?.post) {
        // Post updated, do something with `route.params.post`
        // For example, send the post to the server
    }
}, [route.params?.post]);

    return (
        <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
            <Text>Home Screen</Text>
            <Button title="Go to Details" onPress={()=>navigation.navigate('Details')}
            <Button title="Create Post" onPress={()=>navigation.navigate('CreatePost')}
        </View>
    );
}

function CreatePostScreen({navigation, route}) {
    const [postText, setPostText] = React.useState('');
    return (
        <>
            <TextInput
                multiline
                placeholder="What's on your mind?"
                style={{ height: 200, padding: 10, backgroundColor: 'white' }}
                value={postText}
                onChangeText={setPostText}
            />
            <Button title="Done" onPress={ ()=> navigation.navigate('Home', { post: postText }) } />
        </>
    );
}

function DetailsScreen({navigation}) {
    return (
        <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
            <Text>Details Screen</Text>
            <Button title="Go to Details... again" onPress={()=>navigation.push('Details')}
            <Button title="Go to Home" onPress={()=>navigation.navigate('Home')}
            <Button title="Go back" onPress={()=>navigation.goBack()}
            <Button title="Go back to first screen in stack" onPress={()=>navigation.popToTop()}
        </View>
    );
}

const Stack = createStackNavigator();
    
function App() {
    return (
        <NavigationContainer>
            <Stack.Navigator initialRouteName="Home">
                <Stack.Screen name="Home" component={HomeScreen} />
                <Stack.Screen name="Details" component={DetailsScreen} />
                <Stack.Screen name="CreatePost" component={CreatePostScreen} />
            </Stack.Navigator>
        </NavigationContainer>
    );
}

Bu örneği Snack üzerinde deneyin

Burada "Done" butonuna basıldığıktan sonra Home ekranındaki route.params, navigate ile gönderdiğinzi post'u ekrana yansıtacak şekilde güncellenmiş olacak.

Nested Navigator'lere parametre geçmek

Eğer nested navigator'unuz varsa ona da birazcık farklı bir şekilde parametre gönderebilirsiniz. Örneğin Account ekranınız var ve buna parametre göndermek istiyorsunuz. Parametreleri aşağıdaki gibi geçebilirsiniz:

navigation.navigate('Account', {
    screen: 'Settings',
    params: { user: 'Jane' }
});

Nesting navigator'ler hakkında daha fazla bilgi için o konuya göz atın.

Burada aslında mesela stack navigator içinde stack navigator veya bottom tab navigator gibi başka navigatorler kullanmakla ilgili bi'şeyler bilmeniz gerekiyordu ama dokümantasyon bu örneği önden vermek istemiş. Yani henüz bu işlemi dokümantasyonda görmediğimiz için bu başlıkta ne anlatıldığını anlamamış olmanız doğal.

Parametrelerde ne olmalı?

Parametrelerde nasıl veri gönderdiğimizi anlamak önemli. Parametreler, bir Screen'e verilmiş options prop'u gibidirler. Yalnızca ekranda görünecekleri yapılandırmak için bilgi içermelidirler. Ekranda görüntülenecek tüm verileri iletmekten kaçının (mesela kullanıcı bilgilerini toptan atmak yerine bir userId gönderin). Ayrıca birden çok ekran tarafından kullanılan verileri iletmekten de kaçının çünkü bu tip veriler global olarak tutulmalıdır.

Rota nesnesini bir URL gibi de düşünebilirsiniz. Bir URL'den sayfaya veri iletmek istediğinizde ne tür veriler gönderirsiniz? Mesela bir kullanıcının profil sayfasını göstermek istiyorsanız kullanıcının adını, yaşını, fotoğrafını vb. tüm verilerini iletmezsiniz. Sadece kullanıcının id'sini gönderirsiniz ve diğer verileri bu id yardımıyla elde edersiniz. Bu şekilde, bir URL'de olmaması gerektiğini düşündüğünüz verileri route parametrelerinde de göndermeyin. Yani sayfalara mümkün olduğunca az veri göndermeniz doğru bir yaklaşım olacaktır.

// Bunu yapmayın
navigation.navigate('Profile', {
    user: {
        id: 'jane',
        firstName: 'Jane',
        lastName: 'Done',
        age: 25,
    },
});

Bu şekilde parametre gönderdiğinzi Profile sayfasında fazladan iş yükü olmadan route.params.user diyerek kullanıcı verilerine ulaşmış olmak daha rahat görünüyor olabilir.

Aslında bu bir anti-pattern'dir. Bu tür veriler aslıdna global deponuzda olmalıdır. Çünkü bu verileri başka yerlerde çağırmak da isteyeceksiniz ve proje büyüdüğünde bu iletim yüzünden kod kalabalığı oluşacak, kodun uzunluğu nedeniyle okunabilirliği düşecek, hatalı veri gösterme olasılığı artacak vs.

Neden kullanıcıyı olduğu gibi aktarmak yerine sadece navigation.navigate('Profile', {userId:'jane'}); olarak göndermeniz gerektiğiyle ilgili uzun bir açıklama var ama sanırım zaten konuyu anladınız.


Dokümantasyonda Header bar Ayarları ve Header Butonları konuları vardı ama şu ana kadar attığımız temelle bunları dokümantasyondan anlamak kolaylaşmış olmalı. Bu konuları geçiyorum ve yukarıda anlatırken havada kalmış bir konu olan Nesting Navigators konusuna geçiyorum.


Nesting/iç içe geçmiş navigator'lar

Nesting navigator'ler, bir navigator'un screen'i olarak başka bir navigator kullanmak anlamına geliyor.

    function Home() {
    return (
        <Tab.Navigator>
            <Tab.Screen name="Feed" component={Feed} />
            <Tab.Screen name="Messages" component={Messages} />
        </Tab.Navigator>
    );
}
        
    function App() {
        return (
            <NavigationContainer>
                <Stack.Navigator>
                    <Stack.Screen name="Home" component={ Home } />
                    <Stack.Screen name="Profile" component={Profile} />
                    <Stack.Screen name="Settings" component={Settings} />
                </Stack.Navigator>
            </NavigationContainer>
        );
    }

Yukarıdaki örnekte Home component'i bir tab navigator içeriyor. App component'indeki stack navigator içine Home component'i ile bir tab navigator yerleştirilmiştir:

Nesting navigator'ler, normal component'leri yerleştirmeye çok benzer. İstediğiniz davranışı elde etmek için genellikle birden çok navigator yerleştirmek gerekir.

Nesting navigator'ler davranışı nasıl etkiler?

Navigator'leri yerleştirirken aklınızda bulundurmanız gereken bazı şeyler var:

Her navigator, kendi gezinme geçmişini tutar

Örneğin, nesting navigator'de bir screen içindeyken geri butonuna basığınızda, üst öğe olarak başka bir navigator olsa bile nesting navigator'ün yığını içindeki bir önceki ekrana döner.

Her navigator'un kendi seçenekleri vardır

Örneğin, bir alt navigator içindeki header ayarları üst navigator'ü etkilemez.

Bu tip bir davranışı elde etmek istiyorsanız nested navigator'ler için ekran ayarları kılavuzuna bakın. Bir stack navigator içinde bir tab navigator oluşturuyorsanız ve stack navigator'un header'ında tab navigator'deki etkin ekranın başlığını göstermek istiyorsanız yararlı olabilir.

Navigator'daki her screen'in kendi parametreleri vardır

Örneğin, iç içe geçmiş bir navigator'daki bir screen'e aktarılan parametreler, o ekranın route prop'undadır ve alt navigator'daki bir ekrandan erişilemez.

Bir alt ekrandan ana ekranın parametrelerine erişmeniz gerekiyorsa, parametreleri alt elemanlara (child'lere) iletmek için React Context'i kullanabilirsiniz.

Navigation eylemleri mevcut navigator tarafından işlenir ama işlenemezse üst navigator tarafından işlenmeye çalışılır

Mesela bir nesting navigator içinde navigation.goBack() dediniz. Sadece eğer bulunduğunuz navigator'daki yığının en üst sayfasındaysanız üst navigator'e dönersiniz. navigation.navigate() gibi navigation işlemleri de benzer şekilde çalışır. Komutu verdiğiniz navigator içinde navigation işlemi gerçekleştirilir. Eğer bu navigator'un yığınında bu sayfa bulunamıyorsa üst navigator işlemi gerçekleştirmeye çalışır. Mesela yukarıdaki örnekde tab navigator'deki bir screen içinde navigate('Messages') derseniz tab navigator sizi kendi içindeki Messages screen'ine yönlendirir. Ama siz tutup da tab navigator screen'i içinde navigate('Settings') derseniz tab navigator işi halletmeye çalışır ama böyle bir route barındırmadığı için işi üst navigator devralır ve tab navigator'den üstteki stack navigator'e geçip Settings screen'ine yönlenmiş olursunuz.

Bu da performans sorunu yaratabiliyor. Navigator önce kendi içinde sayfayı arıyor, bulamayınca üst navigator'un yığınını kontrol ediyor. Sonra o yığında gideceğiniz sayfayı en üste alıyor vs vs.

Navigator'e özel fonksiyonlar nested navigator'ler içinde de mevcuttur

Mesela bir drawer navigator içinde bir stack navigator varsa drawer'in openDrawer, closeDrawer, toggleDrawer vb. fonksiyonları stack navigator'un navigator prop'unda mevcuttur. Ama mesela drawer'ın üst navigator'u olarak bir stack navigator'unuz var diyelim. Bu durumda stack navigator içindeki screen'ler bu yöntemlere erişemezler. Çünkü bu yöntemler drawer navigator'ün içinde kalmıştır.

Benzer şekilde, stack navigator'de bir tab navigator varsa, stack navigator içindeki screen'ler navigation prop'unda push ve replace fonksiyonlarını da alacaktır.

Eğer üst öğeden iç içe geçmiş alt navigator'lere/nested navigator'lere eylemler/action'lar göndermeniz gerekiyorsa navigation.dispatch'ı kullanabilirsiniz:

navigation.dispatch(DrawerActions.toggleDrawer());

Nested navigator'ler parent'lerinin/üst öğelerinin olaylarını/event'lerini almazlar

Örneğin bir tab navigator içine yerleştirilmiş bir stack navigator'unuz varsa stack navigator'deki screen'ler navigation.addEventListener kullanılırken (tabPress) gibi üst tab navigator tarafından yayılan olayları almazlar.

Ama siz yine de üst navigator'deki olayları almak için navigation.dangerouslyGetParent() kullanabilirsiniz:

    const unsubscribe = navigation
        .dangerouslyGetParent()
        .addListener('tabPress', (e) => {
          // Do something
    });

Üst navigator'un UI'ı, alt navigator'un üstünde render edilir

Mesela bir drawer navigator'un içinde bir stack navigator yerleştirdiğinizde, drawer'ın, stack navigator'unun header'inin üzerinde göründüğünü göreceksiniz. Ama bir drawer navigator'unu bir stack navigator'unun içine koyarsanız drawer, stack'ın header'inin altında görünecektir. Bu, navigator'unuzu nasıl yerleştireceğinize karar verrirken göz önünde bulundurmanız gereken önemli bir noktadır.

Uygulamanızda, muhtemelen istediğiniz davranışa bağlı olarak şu kalıpları kullanacaksınız:

Nested navigator içinde bir screen'e yönlenme

Şu örneği düşünün:

        function Root() {
            return (
                <Stack.Navigator>
                    <Stack.Screen name="Profile" component={Profile} />
                    <Stack.Screen name="Settings" component={Settings} />
                </Stack.Navigator>
            );
          }
          
        function App() {
            return (
                <NavigationContainer>
                    <Drawer.Navigator>
                        <Drawer.Screen name="Home" component={Home} />
                        <Drawer.Screen name="Root" component={Root} />
                    </Drawer.Navigator>
                </NavigationContainer>
            );
        }

Burada, App bileşeninizden Root stack'a gitmek isteyebilirsiniz.

navigation.navigate('Root');

Bu kod çalışınca Root bileşeni içindeki ilk ekran (Profile) gösterilir. Ancak bazen navigasyon sırasında gösterilmesi gereken ekranı kontrol etmek de isteyebilirsiniz. Bun uyapmak için screen adını parametrelerde geçebilirsiniz:

navigation.navigate('Root', { screen: 'Settings' });

Şimdi, navigasyon sırasında Profile yerine Settings ekranı görüntülenecek.

Nested/iç içe geçmiş gir navigator'de bir ekrana parametre göndermek

params key'ini de geçirirseniz...

navigation.navigate('Root', { screen: 'Settings' , params: { user: 'jane' } });

Snack üzerinde deneyin...

Derinlemesine iç içe geçmiş ekranlar için de benzer bir yaklaşım var. Burada navigasyon için ikinci parametrenin sadece params olduğuna dikkat edin. Böylece aşağıdaki gibi bi'şey yapabilirsiniz:

    navigation.navigate('Root', {
        screen: 'Settings',
        params: {
            screen: 'Sound',
            params: {
                screen: 'Media',
            },
        },
    });

Yukarıdaki durumda, Settings ekranının içine yerleştirilmiş bir navigator içinde bulunan Sound screen'inin içine yerleştirilmiş bir Media screen'ine gidersiniz.

Navigator'deki initial route'yi oluşturmak

Aradığım bi'şeyleri bulmak için buraya kadar geldim ama bir türlü bulamadım...