DEV Community

Cover image for React navigation v5
Jeevan Kishore
Jeevan Kishore

Posted on

React navigation v5

With the launch of react native v5 about 3 months ago backed by neat documentation making life easier with hooks usage and native transitions, we decided to revamp our current navigation.

Problem:

Build a navigation system for a an app serving to showcase news content such as collections and stories with configurable paywall to facilitate pay per article / premium content across devices

Design

Alt Text

  • Main screen consists of bottom tab navigation of about 4 tabs
  • Listing screens to showcase categories / sections
  • Content screens to display content

Architecture:

Alt Text

App.js

The root of your application needs to be enclosed within a navigation controller, the children of which would be the stack of screens. This will helps us separate concerns b/w auth flows such as login stack and other flows in our case

import { NavigationContainer, useLinking } from "@react-navigation/native";
import { createNativeStackNavigator } from "@react-navigation/native-stack";
import { enableScreens } from "react-native-screens";
enableScreens();
const [initialState, setInitialState] = useState();
     <NavigationContainer initialState={initialState} ref={ref}>
         <Stack.Navigator
           screenOptions={{
             headerShown: false
           }}
           initialRouteName="modalStack"
         >
           <Stack.Screen name="modalStack" component={ModalStack} />
           <Stack.Screen name="loginStack" component={LoginStack} />
         </Stack.Navigator>
       </NavigationContainer>

modal-stack.js

The stack is composed of multiple screens which relate in terms of genre in our case.

Alt Text

The noticeable aspect is that the stacks are nested, BottomTabStack is nested within the modal stack and that will be the initial route where our app should land post launch

import React from "react";
import { BottomTabStack } from "../bottom-tab-navigators/bottom-tab-stack";

import { createNativeStackNavigator } from "@react-navigation/native-stack";
import { SCREEN_LIST, STACK_LIST } from "../../constants/screen-mapping";

/* Import screen components such as AuthorsScreen, StoryScreen ... */

const Stack = createNativeStackNavigator();

export const ModalStack = () => (
  <Stack.Navigator
    screenOptions={{
      headerShown: false
    }}
    initialRouteName={STACK_LIST.bottomTabStack}
  >
    <Stack.Screen name={SCREEN_LIST.authorScreen} component={AuthorsScreen} />
    <Stack.Screen name={SCREEN_LIST.storyScreen} component={StoryScreen} />
    <Stack.Screen name={SCREEN_LIST.bookMarkScreen} component={BookMarkScreen} />
    <Stack.Screen name={STACK_LIST.bottomTabStack} component={BottomTabStack} />
  </Stack.Navigator>
);

bottom-tab-stack.js


import React from "react";

import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";

/* Import necessary constants and components.. */

const Tab = createBottomTabNavigator();

const tabBarStyle = {
  activeTintColor: "blue",
  style: {
    paddingTop: 5
  }
};

const TabIcons = (focused, icon) => {
  const iconColor = focused ? "blue" : "black"; /* highlight with a diff color if focused */

  switch (icon) {
    case "home":
      return <HomeIcon color={iconColor} />;
    case "sections":
      return <SectionIcon color={iconColor} />;
    case "search":
      return <SearchIcon color={iconColor} />;
    case "my-app":
      return <MyAppIcon color={iconColor} />;
    default:
      break;
  }
};

export const BottomTabStack = () => (
  <Tab.Navigator tabBarOptions={tabBarStyle}>
    <Tab.Screen
      name={BOTTOM_TAB.bottomHomeTab}
      component={HomeScreen}
      options={{
        tabBarIcon: ({ focused }) => TabIcons(focused, "home"),
        title: "Home",
        tabBarTestID: "test-id-home" /* test id's for use by automation test tools */
      }}
    />
    <Tab.Screen
      name={BOTTOM_TAB.bottomCategoryTab}
      component={CategoryScreen}
      options={{
        tabBarIcon: ({ focused }) => TabIcons(focused, "sections"),
        title: "Sections",
        /* Unique identifier to target category tab */
        tabBarTestID: "test-id-categ"
      }}
    />
    <Tab.Screen
      name={BOTTOM_TAB.bottomSearchTab}
      component={SearchScreen}
      options={{
        tabBarIcon: ({ focused }) => TabIcons(focused, "search"),
        title: "Search",
        tabBarTestID: "test-id-search"
      }}
    />
    <Tab.Screen
      name={BOTTOM_TAB.bottomProfileTab}
      component={ProfileScreen}
      options={{
        tabBarIcon: ({ focused }) => TabIcons(focused, "my-app"),
        title: "My App",
        tabBarTestID: "test-id-profile"
      }}
    />
  </Tab.Navigator>
);


login-stack.js

This stack abstracts the auth-flow.


import React from "react";
import { createNativeStackNavigator } from "@react-navigation/native-stack";

/* Import screen components such as Login, SignUp ... */

const Stack = createNativeStackNavigator();

export const LoginStack = () => (
  <Stack.Navigator
    screenOptions={{
      headerShown: false
    }}
  >
    <Stack.Screen name={SCREEN_LIST.loginScreen} component={Login} />
    <Stack.Screen name={SCREEN_LIST.registrationScreen} component={SignUp} />
    <Stack.Screen name={SCREEN_LIST.forgotPasswordScreen} component={ForgotPassword} />
  </Stack.Navigator>
);


With that being set, we can begin surfing 🏄 through the screens

If we are navigating within a stack:

navigation.navigate("<<screen_name>>", {<<key>>: <<val>>});

If we do need to navigate across stacks:

navigation.navigate("<<stack_name>>", {  
                     screen: <<screen_name>>,
                     params: {<<param_key>> : <<param_val>>}
                   });

There are a lot of ground to cover with react-navigation such as events, lifecycles and much more ..

Will be dropping in a github repo link for an example soon 🔥

If you have questions, let us know in the comments and we are looking forward for your feedback 🍻

Top comments (0)