Create Login Stack using React Navigation

React Navigation, Not only gives us a stack level approach to components but also gives us an option to switch between different stacks (i.e., on demand)

Consider this scenario, Intended app we develop has a Login and Logged in Screen areas. The user first time presented with a Login screen with an option to Register. Only after successful login, we could see the main screen with intended data.

When user enters correct authorisation credentials, The Login view should be removed from stack and only main screen stacks would be accessible after that. Means, If the user press back button (in Android) there should not be any trace of Login Screen.

React Navigation has switchNavigation function to achieve above conditional stacks. Of course certain planing required for it 🙂

Let’s create following components having file names such as,

  • Loading.js
  • Login.js
  • Register.js
  • Main.js
  • Next.js

I am going to group all of them in to 3 stacks.

  • AuthStack (Login, Register), They will be displayed when user has not logged in yet.
  • HomeStack (Main, Next). These will be displayed only after successful login
  • LoadingStack (Loading). Will be displayed irrespective of Logged in or not. But will have a conditional function to check user login status.

The third one i.e., the Loading screen is the lone component of that group. But this is going to do the switching job. As i already explained, It will have a conditional statement and switch the stack based on the login status. If user is logged in, this will make the HomeStack as the root stack. If not, it will load the AuthStack as the root.

Btw, How do we check or persist the login state? I am going to use AyncStorage of React Native for the persistency need. Let’s see in detail

Create the project using $ react-native init MyAuthFlowProj

$ cd MyAuthFlowProj

$ npm install --save react-navigation react-native-gesture-handler @react-native-community/async-storage

Link them

$ reac-native link react-native-gesture-handler @react-native-community/async-storage

Create following files inside a folder ‘screens’. Loading.js, Login.js, Register.js, Home.js and Next.js

Loading.js

import React, {Component} from 'react';
import {View,ActivityIndicator} from 'react-native';
import AsyncStorage from '@react-native-community/async-storage';

export default class Loading extends Component{

    constructor(props){
        super(props);
        this.checkIfLoggedInOrNot();
    }

    checkIfLoggedInOrNot = async () => {
        const username = await AsyncStorage.getItem('username');
        if (username){
            //logged in
            this.props.navigation.navigate('HomeStack');
        }else{
            // yet to login
            this.props.navigation.navigate('AuthStack');
        }
    }

    render(){
        return<View style={{flex:1,justifyContent:'center',alignItems:'center'}}>
            <ActivityIndicator/>
        </View>
    }
}

AsyncStorage.getItem(‘key’) is a promise object. we can get the value by calling along with await (ES6), It is an asynchronous block but we expect it to work in synchronous with other statements of the function i.e, checkIfLoggedInOrNot

In function checkIfLoggedInOrNot we expect username object, which is stored in AsyncStorage, till it retrieves the value, the execution waits and proceeds further conditional check. Also look at async declaration async (). And I’ll add more explanation to Promise object when we discuss about web services topics.

So as per this, If username is retrieved with a non null value, we are going to assume, User is logged in and switch the stack to HomeStack. If null/undefined retrieved (in case no value exists), we change the stack to AuthStack.

Login.js

import React, {Component} from 'react';
import {View,TextInput,Button} from 'react-native';
import style from '../MyStyle';
import AsyncStorage from '@react-native-community/async-storage';

export default class Login extends Component{

    static navigationOptions = {
        title:'Login'
    }

    constructor(props){
        super(props);
        this.state = {username:'',password:''}
    }

    checkLogin = async () => {
        const {username,password} = this.state;
        if (username === 'admin' && password === 'admin'){
            //allow login
            await AsyncStorage.setItem('username',username);
            this.props.navigation.navigate('Loading');
        }else{
            //show error
            await AsyncStorage.clear();
        }
    }

    render(){
        return<View style={style.fullScreenView}>
            <TextInput placeholder="Enter Username"  style={style.textInput} 
            value={this.state.username} keyboardType='default' 
            onChangeText={(text) => this.setState({username:text})}
            />
            <TextInput placeholder="Enter Password"  style={style.textInput} 
            value={this.state.password} keyboardType='default' secureTextEntry={true}
            onChangeText={(text) => this.setState({password:text})}
            />
            <Button title="Login" onPress={this.checkLogin} />
        </View>
    }
}

Register.js

import React, {Component} from 'react';
import {View,Text,Button} from 'react-native';
import style from '../MyStyle';

export default class Register extends Component{

    static navigationOptions = {
        title:'Register'
    }

    constructor(props){
        super(props);
        
    }

    render(){
        return<View style={style.fullScreenView}>
            <Text>Register Screen coming here...</Text>
        </View>
    }
}

Main.js

import React, {Component} from 'react';
import {View,Text,Button} from 'react-native';
import style from '../MyStyle';

export default class Main extends Component{

    static navigationOptions = {
        title:'Home'
    }

    constructor(props){
        super(props);
        
    }
    
    render(){
        return<View style={style.fullScreenView}>
            <Text>Home Screen coming here...</Text>
        </View>
    }
}

Next.js

import React, {Component} from 'react';
import {View,Text,Button} from 'react-native';
import style from '../MyStyle';

export default class Next extends Component{

    static navigationOptions = {
        title:'Next'
    }

    constructor(props){
        super(props);
        
    }
    
    render(){
        return<View style={style.fullScreenView}>
            <Text>Next Screen coming here...</Text>
        </View>
    }
}

I have moved the stylesheet declaration in to a separate file MyStyle.js

import {StyleSheet} from 'react-native';

const style = StyleSheet.create({
    textInput:{
        width:200,
        height:40,
        borderWidth:1,
        borderColor:'green',
        borderRadius:4,
        padding:5,
        margin:10
    },
    fullScreenView:{
        flex:1,
        justifyContent:'center',
        alignItems:'center'
    }
});

export default style;

Except Login.js, Loading.js all the other components carries simple view but with a different title to distinguish. We will expand them in a moment.

We have been talking about the conditional switching of stacks, declaring views under 3 groups. Create MyNavigator.js files

MyNavigator.js

import {createAppContainer,createStackNavigator,createSwitchNavigator} from 'react-navigation';
import Login from './screens/Login';
import Register from './screens/Register';
import Main from './screens/Main';
import Next from './screens/Next';
import Loading from './screens/Loading';

const AuthRoute = createStackNavigator({
    Login:{screen:Login},
    Register:{screen:Register}
});

const MainRoute = createStackNavigator({
    Home:{screen:Main},
    Next:{screen:Next}
});

const StackNavigator = createSwitchNavigator(
    {
        AuthStack:{screen:AuthRoute},
        HomeStack:{screen:MainRoute},
        Loading:{screen:Loading}
    },
    {
        initialRouteName:'Loading'
    }
);

const AppContainer = createAppContainer(StackNavigator);

export default AppContainer;

We have created StackNavigator object from createSwitchNavigator function of React Navigation. This combines 3 different stacks and gives us higher level name of Stacks, We have called them AuthStack, HomeStack and Loading. (Last one being a lone view, we have not explicitly wrapped it with in a createStackNavigator function)

Finally modify the App.js

import React, {Component} from 'react';
import AppContainer from './MyNavigator';


export default class App extends Component{
  render(){
    return<AppContainer/>
  }
}

Fire the android emulator and execute $ react-native run-android

We get the following output

Download source code

Leave a Reply

Your email address will not be published. Required fields are marked *