How Authentication Flow works in React Native apps using React Navigation 4.x

Published on Sep 10, 2019

10 min read

REACT-NATIVE

cover_image

Mobile apps are made of screens that in number can vary depending on the app you are developing. Handling user navigation can be tricky to learn and do in mobile apps, but with dedicated open-source libraries like react-navigation do make the process a lot easier.

React Navigation library is common among React Native developers. It is built with JavaScript, and you can create React components and apply any navigation pattern. On the device, it will give the natural look and feel.

It is up to the developer now, on how to make the best use of navigation between different screens in a React Native app. There are more than one navigation patterns available. If you are starting in the React Native ecosystem, this post will guide you through to use different the patterns of navigation such as Stack and Switch navigation using react-navigation library's latest 4.x.x version.

Table of Contents

🔗
  • Requirements
  • Installing navigation library
  • Create app screens
  • Setup navigation
  • Navigating between two screens
  • Managing authentication flow
  • Conclusion

Requirements

🔗

If you are going to code along, make sure you have already installed the following:

  • Nodejs (>=10.x.x) with npm/yarn installed.
  • expo-cli (>= 3.x.x), previously known as create-react-native-app.
  • Mac users must be running an iOS simulator.
  • Windows/Linux users must be running an Android emulator.

To know more about how to setup and run the simulator or the emulator on your local development environment visit React Native’s official documentation here.

Installing navigation library

🔗

To get started, create a new Expo app using expo-cli with the following command from a terminal window. When asked, choose the blank template.

expo init expo-example
# navigate inside the project directory
cd expo-example

Once inside the project directory, install the following dependencies.

yarn add react-navigation react-navigation-stack
expo install react-native-gesture-handler
react-native-screens

As compared to previous versions of react-navigation, all three navigation patterns have been modularised in their own dependencies. If you are using:

  • stack navigation, then install react-navigation-stack
  • for tabs install react-navigation-tabs
  • for drawer install react-navigation-drawer
  • switch navigation pattern is still under react-navigation and is only used for specific use cases such as authentication flow

More appropriate information about each dependency related to its own navigation pattern can be found in the official docs here.

After installing these dependencies, you can verify that they have been installed by opening the package.json file.

1"dependencies": {
2 "expo": "^34.0.1",
3 "react": "16.8.3",
4 "react-dom": "^16.8.6",
5 "react-native": "https://github.com/expo/react-native/archive/sdk-34.0.0.tar.gz",
6 "react-native-gesture-handler": "~1.3.0",
7 "react-native-reanimated": "~1.1.0",
8 "react-native-screens": "1.0.0-alpha.22",
9 "react-native-web": "^0.11.4",
10 "react-navigation": "4.0.0",
11 "react-navigation-stack": "1.5.1"
12 },

Create App Screens

🔗

I like to arrange different setups and related files under the folder structure. Here is how it is going to look like at the end of this tutorial. It is also a good practice to organize or give structure to your project.

1

The three files inside the screens folder are going to be functional components for now, with some dummy text to display. Create these files with the following code snippets.

For Home.js:

1import React from 'react';
2import { StyleSheet, Text, View } from 'react-native';
3
4export default function Home() {
5 return (
6 <View style={styles.container}>
7 <Text>Home</Text>
8 </View>
9 );
10}
11
12const styles = StyleSheet.create({
13 container: {
14 flex: 1,
15 backgroundColor: '#fff',
16 alignItems: 'center',
17 justifyContent: 'center'
18 }
19});

For Login.js:

1import React from 'react';
2import { StyleSheet, Text, View } from 'react-native';
3
4export default function Login() {
5 return (
6 <View style={styles.container}>
7 <Text>Login</Text>
8 </View>
9 );
10}
11
12const styles = StyleSheet.create({
13 container: {
14 flex: 1,
15 backgroundColor: '#fff',
16 alignItems: 'center',
17 justifyContent: 'center'
18 }
19});

For Signup.js:

1import React from 'react';
2import { StyleSheet, Text, View } from 'react-native';
3
4export default function Signup() {
5 return (
6 <View style={styles.container}>
7 <Text>Signup</Text>
8 </View>
9 );
10}
11
12const styles = StyleSheet.create({
13 container: {
14 flex: 1,
15 backgroundColor: '#fff',
16 alignItems: 'center',
17 justifyContent: 'center'
18 }
19});

The idea here is to create a foundation of three different screens and mock a login/signup and main (in the current case, the home screen) screen navigation pattern. This is a common pattern in most mobile apps where the user has to either signup or login before accessing the rest of the application.

Setup Navigation

🔗

After creating these three screens, create a new directory called navigation. Inside this, create three new files:

  • index.js
  • AppNavigation.js
  • AuthNavigation.js

Let us setup the AppNavigation first since it will contain only one screen. Open up the file and add the following code.

1//AppNavigation.js
2import { createStackNavigator } from 'react-navigation-stack';
3import Home from '../screens/Home';
4
5const AppNavigation = createStackNavigator(
6 {
7 Home: { screen: Home }
8 },
9 {
10 initialRouteName: 'Home'
11 }
12);
13
14export default AppNavigation;

Stack Navigation provides your app to navigate between screens, where each new screen is placed on the top of the previous one. It is literally like a stack and hence the name. This is done by createStackNavigator function. A route configuration object is passed to this function. The Home route corresponds to the Home.js component.

On an iOS device, a new screen slides from the right, and on Android, it fades from the bottom.

Next, edit AuthNavigation.js file.

1//AuthNavigation.js
2import { createStackNavigator } from 'react-navigation-stack';
3import Login from '../screens/Login';
4import Signup from '../screens/Signup';
5
6const AuthNavigation = createStackNavigator(
7 {
8 Login: { screen: Login },
9 Signup: { screen: Signup }
10 },
11 {
12 initialRouteName: 'Login'
13 }
14);
15
16export default AuthNavigation;

Similarly, in AuthNavigation two screens, login and signup are passed. In the second object that is passed to createStackNavigator function, the initialRouteName indicates that when this navigation file runs, the first screen that will be shown is going to be Login. In other words, it is used to set a default screen to whatever the value initialRouteName is set to.

In AppNavigation since there is only one screen, so it will always show Home screen whether to pass the initialRouteName in that file or not. Next, open index.js file in the same directory and add the following code.

1//index.js
2import { createAppContainer } from 'react-navigation';
3import AuthNavigation from './AuthNavigation';
4
5const AppContainer = createAppContainer(AuthNavigation);
6
7export default AppContainer;

The createAppContainer function is responsible for managing the navigation state of the app and links the app to the top-level navigator. The navigation state comes in handy when you are passing data between two screens.

Lastly, open the App.js file and use AppContainer to be the top-level component.

1//App.js
2import React from 'react';
3import AppContainer from './navigation';
4
5export default function App() {
6 return <AppContainer />;
7}

Now open your app in a simulator device by executing the command expo start from a terminal window. You will see that it shows only the Login screen.

Notice the empty space at the top of the screen? That is the header section. When using Stack Navigation pattern, each screen is assigned a header automatically. If you do not require to use it, you can set the headerMode property to the value of none to createStackNavigator function. Open AuthNavigation.js to edit.

1// AuthNavigation.js
2const AuthNavigation = createStackNavigator(
3 {
4 Login: { screen: Login },
5 Signup: { screen: Signup }
6 },
7 {
8 initialRouteName: 'Login',
9 headerMode: 'none'
10 }
11);

You can read more about app containers here.

Navigating between two screens

🔗

Right now, there is no way you can navigate from the Login to the Signup screen. To do so, let us use this.props.navigation. Each screen component in the app using react-navigation library is automatically provided with the navigation prop. It further has different reference values to navigate between different screens directly from a screen.

To transit between login to signup, create a button like below and pass an onPress prop to it in Login.js file. The value of this prop is going to hold the navigation prop reference.

1//Login.js
2
3//import Button
4import { StyleSheet, Text, View, Button } from 'react-native';
5
6export default class Login extends React.Component {
7 render() {
8 return (
9 <View style={styles.container}>
10 <Text>Login</Text>
11 <Button
12 title="Go to Signup"
13 onPress={() => this.props.navigation.navigate('Signup')}
14 />
15 </View>
16 );
17 }
18}

Passing the name of the route as the first parameter to navigation.navigate() is necessary. Now go back to the simulator, and you will find a new button. Press the button, and it will take you to the Signup screen component.

Similarly, you can add a way to navigate back to the login screen component from the signup.

1//Signup.js
2
3export default class Signup extends React.Component {
4 goToLogin = () => this.props.navigation.navigate('Login');
5 render() {
6 return (
7 <View style={styles.container}>
8 <Text>Signup</Text>
9 <Button title="Go to Login" onPress={this.goToLogin} />
10 </View>
11 );
12 }
13}

Here is the output.

Managing Authentication Flow

🔗

In React Navigation, to manage authentication flow, Switch Navigator is used. This navigation pattern only loads one screen at a time, and there is no back functionality by default. It resets the initial route when switching between the screens. To get started open index.js file, import createSwitchNavigator from react-navigation and add the following code.

1//index.js
2import { createSwitchNavigator, createAppContainer } from 'react-navigation';
3import AuthNavigation from './AuthNavigation';
4import AppNavigation from './AppNavigation';
5
6const SwitchNavigator = createSwitchNavigator(
7 {
8 Auth: AuthNavigation,
9 App: AppNavigation
10 },
11 {
12 initialRouteName: 'Auth'
13 }
14);
15
16const AppContainer = createAppContainer(SwitchNavigator);
17
18export default AppContainer;

Note that, the AppContainer is still being exported from the file, but it now accepts SwitchNavigator as the parameter. Like the createStackNavigator, createSwitchNavigator also accepts route config as the first parameter and the configuration values as the second. The route config is going to be done between the authentication navigation screens and the other screens related to the app.

Import both AuthNavigation and AppNavigation and set the Auth as the initial route. This means that the login screen is going to be shown for the when apps load for the first time.

Let us mock the behavior of logging into the app and see what happens when the user successfully logs in. Open Login.js file, define an initial state with two properties: email and password.

1//Login.js
2import React from 'react';
3import { StyleSheet, View, Button, TextInput } from 'react-native';
4
5export default class Login extends React.Component {
6 state = {
7 email: '',
8 password: ''
9 };
10
11 handleEmailChange = email => {
12 this.setState({ email });
13 };
14
15 handlePasswordChange = password => {
16 this.setState({ password });
17 };
18
19 onLogin = async () => {
20 const { email, password } = this.state;
21 try {
22 if (email.length > 0 && password.length > 0) {
23 this.props.navigation.navigate('App');
24 }
25 } catch (error) {
26 alert(error);
27 }
28 };
29
30 goToSignup = () => this.props.navigation.navigate('Signup');
31 render() {
32 const { email, password } = this.state;
33
34 return (
35 <View style={styles.container}>
36 <View style={{ margin: 10 }}>
37 <TextInput
38 name="email"
39 value={email}
40 placeholder="Enter email"
41 autoCapitalize="none"
42 onChangeText={this.handleEmailChange}
43 />
44 </View>
45 <View style={{ margin: 10 }}>
46 <TextInput
47 name="password"
48 value={password}
49 placeholder="Enter password"
50 secureTextEntry
51 onChangeText={this.handlePasswordChange}
52 />
53 </View>
54 <Button title="Login" onPress={this.onLogin} />
55 <Button title="Go to Signup" onPress={this.goToSignup} />
56 </View>
57 );
58 }
59}
60
61const styles = StyleSheet.create({
62 container: {
63 flex: 1,
64 backgroundColor: '#fff',
65 alignItems: 'center',
66 justifyContent: 'center'
67 }
68});

The onLogin handler function allows the user to navigate to the Home screen only if the email and the password fields are not empty. It is used on the onPress prop for the following button.

1<Button title="Login" onPress={this.onLogin} />

Look at the complete demo below.

Conclusion

🔗

The authentication flow works! By following this tutorial, you have learned how to use the latest react-navigation library to manage and mimic an authentication flow in a React Native app. Using the current knowledge, in the next post, you are going to build some actual forms in React Native apps with proper styling and validation using awesome libraries like Formik and Yup. I hope this post works as fundamental for the next one.

You can find the complete code used in this tutorial at the Github repo here.

Important resources from this post:

Originally published at Heartbeat


More Posts

Browse all posts

Aman Mittal author

I'm a software developer and a technical writer. On this blog, I write about my learnings in software development and technical writing.

Currently, working maintaining docs at 𝝠 Expo. Read more about me on the About page.


Copyright ©  2019-2024 Aman Mittal · All Rights Reserved.