Nicolás MartínezMay 30, 2017

React Native Navigation

Traditionally, the de-facto solutions for multiplatform mobile apps were based on hybrid frameworks like Ionic, which uses web technologies (HTML, CSS, JS) to write and render the app on any device. It’s a great framework, it makes its best in order to reproduce a near-native behavior to provide the best user experience, but sometimes our projects require the real look and feel of an iOS and Android app.

In the last year, we’ve been working on some web projects based on React, the well-known web framework made by Facebook, and it was a satisfying experience. This way, we decided to research on the other big-boy Facebook’s framework: React Native. It basically let us build native apps based on JavaScript and React. Cool, right?

It today’s post we will show a brief example of a navigation implementation with React Native, and how it reproduce a real look and feel native navigation experience on both Android and iOS.

In order to implement the navigation, we’re gonna use a community solution recommended by Facebook: React Navigation. React Navigation provides many navigation styles like stack, tabs and drawer with a real native feeling. Now we’re gonna present a simple example of a stack navigation.

Before starting we must set up all the development environment of React Native and create a project as explained here.

Once the project is created, we start defining the app navigation using the Stack Navigator. The Stack Navigator implements transitions betweens screens, where each screen is pushed in the top of the stack. In Android apps, the screens fade in from the bottom, and in iOS apps the screens slide in from the right.

AppNavigation.js:

import React from 'react';
import { StackNavigator } from 'react-navigation'
import FirstScreen from './FirstScreen'
import SecondScreen from './SecondScreen'

const AppNavigation = StackNavigator({
  First: {
    screen: FirstScreen,
    navigationOptions: {
      title: 'First Screen',
    }
  },
  Second: {
    screen: SecondScreen,
    navigationOptions: {
      title: 'Second Screen',
    }
  }
})

export default AppNavigation

Here you will notice that there are two screens in the navigator: FirstScreen and SecondScreen, which are just two regular React components. Also, you should notice the First and Second keys on the object received by the StackNavigator constructor. These keys define the names of the screens in order to perform transitions, as we’re gonna see later. Finally, the navigationOptions inside each screen object defines many properties for the screen, such as header color and title. You can find every customizable property you can define for the screen here.

FirstScreen.js:

import React, { Component } from 'react';
import {
  View,
  Text,
  StyleSheet,
  Button
} from 'react-native';

export default class FirstScreen extends Component {
  render() {
    const { navigate } = this.props.navigation;

    return (
      <View style={styles.container}>
        <Text style={styles.title}>Welcome to the Native Navigation Example!</Text>
        <Button
          title="Go to second screen"
          onPress={() => navigate('Second')}
        />
      </View>
    );
  }
}

const styles = StyleSheet.create({
  title: {
    fontSize: 18
  },
  container: {
    alignItems: 'center',
    paddingTop: 50
  }
});

Lets take a look at this. First, you will notice there is a navigation prop in the component with a navigate function inside. This prop is provided by the navigator in order to make transitions from one screen to the other, and receives as a parameter the name of the screen defined in the navigator. Once the button is pressed, we execute the prop in order to make the transition.

SecondScreen.js:

import React, { Component } from 'react';
import {
  View,
  Text,
  StyleSheet,
} from 'react-native';

export default class SecondScreen extends Component {
  render() {
    return (
      <View style={styles.container}>
        <Text>This is the second screen!</Text>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    alignItems: 'center',
    paddingTop: 50
  }
});

In order to run this example, we must set the AppNavigator component as the entry point of both Android and iOS apps, so the following code must be placed inside both index.android.js and index.ios.js

import React, { Component } from 'react';
import { AppRegistry } from 'react-native';
import AppNavigation from './app/AppNavigation'

export default class NativeNavigationExample extends Component {
  render() {
    return <AppNavigation/>
  }
}

AppRegistry.registerComponent('NativeNavigationExample', () => NativeNavigationExample);

Lets take a look the result. In order to run the apps, we must use the react-native CLI.

To run the iOS app: react-native run-ios

To run the Android app: react-native run-android

iOS result:
Android result:

As you can see, both navigations follows their respectives native look and feel.

Passing parameters between screens

Now, can we pass some data from one screen to the next one in the stack? Yes, we can do this and very easily.

In order to do this, we’re gonna add a new screen to the navigation stack, that will render its content based on a parameter received from the previous screen.

ThirdScreen.js:

import React, { Component } from 'react';
import {
  View,
  Text,
  StyleSheet,
  Image,
} from 'react-native';
const octobot = require('./images/full_pulpo.png')

export default class ThirdScreen extends Component {
  render() {
    const { navigate } = this.props.navigation;
    const { params } = this.props.navigation.state;

    return (
      <View style={styles.container}>
        <Text> This is the third screen!</Text>
        { params.showOctobot && <Image source={octobot}/> }
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    flexDirection: 'column',
    padding: 30,
    alignItems: 'center',
  }
});

Now, lets make some changes on the SecondScreen component in order to implement the functionality to provide data as parameters to the new screen.

SecondScreen.js:

import React, { Component } from 'react';
import {
  View,
  Text,
  StyleSheet,
  Button,
  Switch
} from 'react-native';

export default class SecondScreen extends Component {
  state = {
    showOctobot: false
  }
  render() {
    const { params } = this.props.navigation.state;
    const { navigate } = this.props.navigation;
    return (
      <View style={styles.container}>
        <Text>Show Octobot?</Text>
        <Switch
          value={this.state.showOctobot}
          onValueChange={(value) => this.setState({showOctobot: value})}
        />
        <Button
          onPress={() => navigate('Third', {showOctobot: this.state.showOctobot})}
          title="Go to the third screen"
        />
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    flexDirection: 'column',
    alignItems: 'center',
    paddingTop: 50
  }
});

As you can see, the navigate functions also accepts a JavaScript object as data parameter to the next screen.

Finally, we must add the new screen to the stack navigator like we did previously:

const AppNavigation = StackNavigator({
  First: {
    screen: FirstScreen,
    navigationOptions: {
      title: 'First Screen',
    }
  },
  Second: {
    screen: SecondScreen,
    navigationOptions: {
      title: 'Second Screen',
    }
  },
  Third: {
    screen: ThirdScreen,
    navigationOptions: {
      title: 'Third Screen'
    }
  }
})

Lets run again the two apps with the react-native CLI and see the results.

iOS result:
Android result:

As you can see, implementing a native navigation and passing parameters from one screen to each other is very straightforward.

The full example can be found here. Check it out!