React Native - Navigation

In this chapter we will show you how to set up navigation in React Native app.
Keep close attention since we will refactor lot of the code but we will show you interesting features.
All previous chapters used MyContainerComponent and MyPresentationalComponent. We did this to get comfortable with separation of logic and view.
Now that you grasped the concept we will use real life examples in our following chapters.
We will explain our files and folders in following list −
Router.js − This is our Router component. We will place it inside src/components folder.
Router.js − This is our Router component. We will place it inside src/components folder.
home − Home page folder. Path to this folder is src/components/home. We will create two components here HomeContainer.js and HomeButton.js
home − Home page folder. Path to this folder is src/components/home. We will create two components here HomeContainer.js and HomeButton.js
about − About page folder. Path to this folder is src/components/about. We will have AboutContainer.js and AboutButton.js inside.
about − About page folder. Path to this folder is src/components/about. We will have AboutContainer.js and AboutButton.js inside.
There is a lot of things going on in example below.
Navigator − This element handles routing. We are setting initialRoute, renderScene and navigatorBar. We will go through these props below.
Navigator − This element handles routing. We are setting initialRoute, renderScene and navigatorBar. We will go through these props below.
initialRoute − We are setting which route will be rendered first when we start the app. This is just an object where we are setting name (used for routing) and title (used for setting title).
initialRoute − We are setting which route will be rendered first when we start the app. This is just an object where we are setting name (used for routing) and title (used for setting title).
renderScene − This is function for calculating routes. We are using route.name for this. If you check initialRoute you will see that name: 'Home' is the same as route.name == 'Home' in renderScene. This is how the app knows that initial route is Home.
We will also pass navigator and ...route.passProps so we can access everything inside rendered component.
renderScene − This is function for calculating routes. We are using route.name for this. If you check initialRoute you will see that name: 'Home' is the same as route.name == 'Home' in renderScene. This is how the app knows that initial route is Home.
We will also pass navigator and ...route.passProps so we can access everything inside rendered component.
NavigationBar − We are using NavigationBarRouteMapper prop so we can add title and buttons for navigation bar. Left button will be used as a back button while right button will only be present if route.openMenu exists. You will see more on this inside Home component.
NavigationBar − We are using NavigationBarRouteMapper prop so we can add title and buttons for navigation bar. Left button will be used as a back button while right button will only be present if route.openMenu exists. You will see more on this inside Home component.
import React, { Component } from 'react'

import {
   StyleSheet,
   Text,
   Navigator,
   TouchableOpacity
} from 'react-native'

import AboutContainer from './about/AboutContainer'
import HomeContainer from './home/HomeContainer'

export default class Router extends Component {
   constructor() {
      super()
   }
   render() {
      return (
         <Navigator
            initialRoute = {{ name: 'Home', title: 'Home' }}
            renderScene = { this.renderScene }
            navigationBar = {
               <Navigator.NavigationBar
                  style = { styles.navigationBar }
                  routeMapper = { NavigationBarRouteMapper } />
            }
         />
      );
   }
   renderScene(route, navigator) {
      if(route.name == 'Home') {
         return (
            <HomeContainer
               navigator = {navigator}
               {...route.passProps} 
            />
         )
      }
      if(route.name == 'About') {
         return (
            <AboutContainer
               navigator = {navigator}
               {...route.passProps} 
            />
         )
      }
   }
}

var NavigationBarRouteMapper = {
   LeftButton(route, navigator, index, navState) {
      if(index > 0) {
         return (
            <TouchableOpacity
               onPress = {() => { if (index > 0) { navigator.pop() } }}>
               <Text style={ styles.leftButton }>
                  Back
               </Text>
            </TouchableOpacity>
         )
      }
      else { return null }
   },
   RightButton(route, navigator, index, navState) {
      if (route.openMenu) return (
         <TouchableOpacity
            onPress = { () => route.openMenu() }>
            <Text style = { styles.rightButton }>
               { route.rightText || 'Menu' }
            </Text>
         </TouchableOpacity>
      )
   },
   Title(route, navigator, index, navState) {
      return (
         <Text style = { styles.title }>
            {route.title}
         </Text>
      )
   }
};

const styles = StyleSheet.create({
   navigationBar: {
      backgroundColor: 'blue',
   },
   leftButton: {
      color: '#ffffff',
      margin: 10,
      fontSize: 17,
   },
   title: {
      paddingVertical: 10,
      color: '#ffffff',
      justifyContent: 'center',
      fontSize: 18
   },
   rightButton: {
      color: 'white',
      margin: 10,
      fontSize: 16
   }
})
This is our container component. We are passing goToAbout prop to HomeButton. Notice openMenu: this.openMenu inside push function. This will enable Menu button on About screen.
src/components/home/HomeContainer.jss
import React, { Component } from 'react'
import {
   View
} from 'react-native'
import HomeButton from './HomeButton'

export default class HomeContainer extends Component {
   constructor() {
      super()
   }
   render() {
      return (
         <View>
            <HomeButton goToAbout = {this.goToAbout}/>
         </View>
      )
   }
   openMenu = () =>{
      alert("Menu button pressed!")
   }
   goToAbout = () => {
      this.props.navigator.push({
         name: 'About',
         title: 'About',
         openMenu: this.openMenu
      });
   }
}
HomeButton is presentational component. It handles styling and calls goToAbout function passed as a prop from HomeContainer.
src/components/home/HomeButton.js
import React, { Component } from 'react'
import {
   View,
   Text,
   TouchableOpacity,
   StyleSheet
} from 'react-native'

export default HomeButton = (props) => {
   return (
      <View style = {styles.container}>
         <TouchableOpacity
            style = {styles.button}
            onPress = {props.goToAbout}>
            <Text>
               Go To About
            </Text>
         </TouchableOpacity>
      </View>
   )
}

const styles = StyleSheet.create ({
   container: {
      marginTop: 100,
      flexDirection: 'column',
      justifyContent: 'center',
      alignItems: 'center'
   },
   button: {
      borderWidth: 1,
      padding: 10,
      borderColor: 'black'
   }
})
This is just an empty component used for About screen.
src/components/about/AboutContainer.js
import React, { Component } from 'react'
import {
   View,
} from 'react-native'

export default class AboutContainer extends Component {
   constructor() {
      super()
   }
   render() {
      return (
         <View>
         
         </View>
      )
   }
}
To be able to use router inside our app, we will place it inside index.ios.js and index.android.js
index.ios.js and index.android.js
import React, { Component } from 'react'
import {
   AppRegistry,
   View
} from 'react-native'
import Router from './src/components/Router'

class reactTutorialApp extends Component {
   render() {
      return (
         <Router />
      )
   }
}

AppRegistry.registerComponent('reactTutorialApp', () => reactTutorialApp)
Now we can run the app. Home screen will be shown.
We can press the button to open About screen.
Notice that Back and Menu button appear inside navigation bar.