React Native Navigation

What is React Native Navigation? 

The community solution to navigation is the standalone library that allows developers to set up the screens of an app with the help of a few lines of codes. React navigation provides a straightforward navigation solution, with the ability to present common stack navigation and tabbed navigation patterns on both iOS and Android. 

Installation and setup

We first need to install react navigation in our projects by using the following code:

npm install @react-navigation/native @react-navigation/stack

The next step would be to install the dependencies using expo: 

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

In case you have a bare React Native project, install the dependencies with npm:

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

 

While using iOS, you must make sure that you have Cocoapods installed, after which you will install the pods to complete installation. 

cd ios

pod install

cd ..

 

To finalize installation of react-native-gesture-handler, add the following at the top of your entry file, such as index.js or app.js

import ‘react-native-gesture-handler’;

 The last step of installation would be to wrap it all up in the NavigationContainer. 

import ‘react-native-gesture-handler’;

import * as React from ‘react’;

import { NavigationContainer } from ‘@react-navigation/native’;

 

const App = () => {

  return (

    <NavigationContainer>

      {/* Rest of your app code */}

    </NavigationContainer>

  );

};

 

export default App;

 

We are now ready to build and run our app.

 

 

Stack Navigation with React Navigation

Stack navigation is one of the navigation techniques provided by react. We will start with a basic App component in our src/App.js file without much style. 

Enter the below code:

import React from ‘react’;

import { StyleSheet, Text, View } from ‘react-native’;

const styles = StyleSheet.create({

  container: {

flex: 1,

alignItems: ‘center’,

justifyContent: ‘center’,

  },

});

const App = () => {

  return (

<View style={styles.container}>

   <Text>Hello React Native!</Text>

</View>

  );

};

export default App;

 

After this, we need to implement two screens (or pages) which will then allow us to navigate from one to another eventually. We create both the screen components in their dedicated folders. 

 

First, src/screens/Landing/index.js:

import React from ‘react’;

import { View, Text, StyleSheet } from ‘react-native’;

const styles = StyleSheet.create({

  container: {

flex: 1,

alignItems: ‘center’,

justifyContent: ‘center’,

  },

});

const LandingScreen = () => {

  return (

<View style={styles.container}>

   <Text>Public Landing Screen</Text>

</View>

  );

};

export default LandingScreen;

And second, src/screens/Home/index.js:

import React from ‘react’;

import { View, Text, StyleSheet } from ‘react-native’;

const styles = StyleSheet.create({

  container: {

flex: 1,

alignItems: ‘center’,

justifyContent: ‘center’,

  },

});

const HomeScreen = () => {

  return (

<View style={styles.container}>

   <Text>Protected Home Screen</Text>

</View>

  );

};

export default HomeScreen;

 

You can see that both the screens are public for now. 

 

With both components implemented, we want to use them in App component, but to do so, we need to install dependencies for the Stack Navigation by using the given code: 

expo install @react-navigation/stack

 

After this, we will create the Stack Navigation in our src/App.js component by using both the screen components:

import React from ‘react’;

import { NavigationContainer } from ‘@react-navigation/native’;

import { createStackNavigator } from ‘@react-navigation/stack’;

 

import LandingScreen from ‘./screens/Landing’;

import HomeScreen from ‘./screens/Home’;

 

const RootStack = createStackNavigator();

 

const App = () => {

  return (

    <NavigationContainer>

      <RootStack.Navigator>

        <RootStack.Screen name=”Landing component={LandingScreen} />

        <RootStack.Screen name=”Home component={HomeScreen} />

      </RootStack.Navigator>

    </NavigationContainer>

  );

};

 

export default App;

 

All navigation components will have to use NavigationContainer as a wrapping component. This is also called RootStack as it is the first level of the navigational hierarchy. 

 

Navigating with React Navigation- 

Now we have two pages but no way to navigate from one page to another. We will now learn how to do so. 

By using the button component, we can execute the react navigation by pressing it:

import React from ‘react’;

import { View, Text, Button, StyleSheet } from ‘react-native’;

const LandingScreen = ({ navigation }) => {

  return (

<View style={styles.container}>

   <Text>Public Landing Screen</Text>

   <Button

     title=”Go to Home

     onPress={() => navigation.navigate(‘Home’)}

   />

</View>

  );

};

export default LandingScreen;

 

Check your app again. You should be able to go from the initially rendered landing screen to Home screen. A bonus of the stack navigation is the back button on the Home Screen, which allows you to navigate back from Home to Landing screen, without further implementations on the home screen. Let us introduce another screen on the app:

 

 

Instead of navigating to home screen, we will navigate to the sign on screen.

import React from ‘react’;

import { View, Text, Button, StyleSheet } from ‘react-native’;

const LandingScreen = ({ navigation }) => {

  return (

<View style={styles.container}>

   <Text>Public Landing Screen</Text>

   <Button

     title=”Go to Sign In

     onPress={() => navigation.navigate(‘Sign In’)}

   />

</View>

  );

};

export default LandingScreen;

 

The location of the component for the sign in screen is,

new src/screens/SignIn/index.js file:

import React from ‘react’;

import { View, Text, StyleSheet } from ‘react-native’;

const styles = StyleSheet.create({

  container: {

flex: 1,

alignItems: ‘center’,

justifyContent: ‘center’,

  },

});

const SignInScreen = () => {

  return (

<View style={styles.container}>

   <Text>Public Sign In Screen</Text>

</View>

  );

};

export default SignInScreen;

 

Before the new page can work as expected, you have to use it as a new screen in the Stack Navigation which is located in our app component: 

 

import React from ‘react’;

import { NavigationContainer } from ‘@react-navigation/native’;

import { createStackNavigator } from ‘@react-navigation/stack’;

import LandingScreen from ‘./screens/Landing’;

import SignInScreen from ‘./screens/SignIn’;

import HomeScreen from ‘./screens/Home’;

const RootStack = createStackNavigator();

const App = () => {

  return (

<NavigationContainer>

   <RootStack.Navigator>

     <RootStack.Screen name=”Landing component={LandingScreen} />

     <RootStack.Screen name=”Sign In component={SignInScreen} />

     <RootStack.Screen name=”Home component={HomeScreen} />

   </RootStack.Navigator>

</NavigationContainer>

  );

};

export default App;

 

We now have three pages and while the two pages should be public, the home screen page should be made protected. This means that it should only be accessible by an authenticated user. Hence there is no direct navigation from Signin screen to Home Screen. 

 

In order to bridge this gap, we will have to implement a pseudo sign in mechanism. 

import React from ‘react’;

import { View, Text, Button, StyleSheet } from ‘react-native’;

const SignInScreen = ({ onSignIn }) => {

  return (

<View style={styles.container}>

   <Text>Public Sign In Screen</Text>

   <Button title=”Sign In onPress={onSignIn} />

</View>

  );

};

export default SignInScreen;

 

 

 

 

 

To pass a new handler to the SignIn screen adjust how the component is passed to the STackNavigation:

const App = () => {

  const [isAuthenticated, setIsAuthenticated] = React.useState(false);

  const handleSignIn = () => {

// TODO implement real sign in mechanism

    setIsAuthenticated(true);

  };

  return (

<NavigationContainer>

   <RootStack.Navigator>

     <RootStack.Screen name=”Landing component={LandingScreen} />

     <RootStack.Screen name=”Sign In“>

       {(props) => (

         <SignInScreen {…props} onSignIn={handleSignIn} />

       )}

     </RootStack.Screen>

     <RootStack.Screen name=”Home component={HomeScreen} />

   </RootStack.Navigator>

</NavigationContainer>

  );

};

 

We can now switch screens to sign in. All that is left is to give the user access to the protected are and this can be done by implementing the following code: 

const App = () => {

  const [isAuthenticated, setIsAuthenticated] = React.useState(false);

  const handleSignIn = () => {

// TODO implement real sign in mechanism

    setIsAuthenticated(true);

  };

  return (

<NavigationContainer>

   <RootStack.Navigator>

     {isAuthenticated ? (

       <RootStack.Screen name=”Home component={HomeScreen} />

     ) : (

       <>

         <RootStack.Screen

           name=”Landing

           component={LandingScreen}

         />

         <RootStack.Screen name=”Sign In“>

           {(props) => (

             <SignInScreen {…props} onSignIn={handleSignIn} />

           )}

         </RootStack.Screen>

       </>

     )}

   </RootStack.Navigator>

</NavigationContainer>

  );

};

 

We could also implement a SignOut screen and place the sign out option on the header of the screen. 

 

import React from ‘react’;

import { Button } from ‘react-native’;

const App = () => {

  const [isAuthenticated, setIsAuthenticated] = React.useState(false);

  const handleSignIn = () => {

// TODO implement real sign in mechanism

    setIsAuthenticated(true);

  };

  const handleSignOut = () => {

// TODO implement real sign out mechanism

    setIsAuthenticated(false);

  };

  return (

<NavigationContainer>

   <RootStack.Navigator>

     {isAuthenticated ? (

       <RootStack.Screen

         name=”Home

         component={HomeScreen}

         options={{

           headerRight: () => (

             <Button onPress={handleSignOut} title=”Sign Out />

           ),

         }}

       />

     ) : (

       <>

         <RootStack.Screen

           name=”Landing

           component={LandingScreen}

         />

         <RootStack.Screen name=”Sign In“>

           {(props) => (

             <SignInScreen {…props} onSignIn={handleSignIn} />

           )}

         </RootStack.Screen>

       </>

     )}

   </RootStack.Navigator>

</NavigationContainer>

  );

};

 

A sign out button too appears now and you can navigate from one screen to another in this basic software.