react-navigation V5

2020-06-10 hit count image

Let's see how to install react-navigaion V5 and how to use it.

Outline

react-navigation had big changes in V5. V4 was similar with V3, but V5 is like another Navigation. SO, I’m writing a blog post about react-navigation V5.

If you want to know previous versions(V3, V4), see the link below.

Below is react-navigation V5 official site.

Let’s see how to use react-navigation V5!

Create react-native project

The source code in this blog post is applied typescript and styled-components. If you want to know how to install typescript and styled-components, see the links below.

Install react-navigation V5

Execute the command below to install react-navigation v5.

npm install --save @react-navigation/native

Also, execute the command below to install required libraries for react-navigation V5.

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

After installing, go to iOS folder and execute the command below.

# cd ios
pod install

Install additional Navigations

From react-navigation V4, we need to install Navigations to use it. V5 is same. We need to install additional navigaiton libraries to use Navigations.

Stack Navigation

Stack Navigation is like below.

React Navigation V5 stack Navigation

[picture: reactnavigation.org Official site]

Execute the command below to install an additional library to use Stack Navigation.

npm install --save @react-navigation/stack

Drawer Navigation

Drawer Navigation is mainly used for Menu. Normally, Users swipe the screen to show Drawer Navigation.

React Navigation V5 drawer Navigation

[picture: reactnavigation.org Official site]

Execute the command below to install an additional library to use Drawer Navigation.

npm install --save @react-navigation/drawer

Bottom Tab Navigation

Bottom Tab Navigation looks like below.

React Navigation V5 bottom-tabs-demo Navigation

[Picture: reactnavigation.org Official site]

Execute the command below to install an additional library to use Bottom Tab Navigation.

npm install --save @react-navigation/bottom-tabs

Material Bottom Tab Navigation

Material Bottom Tab Navigation is Bottom Tab Navigation with Google Material design like below.

React Navigation V5 Material Bottom Tab Navigation

[picture: reactnavigation.org Official site]

Execute the command below to install an additional library to use Material Bottom Tab Navigation.

npm install --save @react-navigation/material-bottom-tabs react-native-paper

And we need to install react-native-vector-icons library to use this Navigation. see the link below to install react-native-vector-icons.

Material Top Tab Navigation

Material Top Tab Navigation is Top Tab navigation with Google Material design like below.

React Navigation V5 material-top-tabs Navigation

[그림: reactnavigation.org 공식 홈페이지]

Execute the command below to isntall an additional library to use Material Top Tab Navigation.

npm install --save @react-navigation/material-top-tabs react-native-tab-view

How to use

There are details about how to use react-navigation in Official site. You can see the details on the link below.

I’ve made a repository by referrering the official site contents. If you see the repository before making applications, it helps you think about a navigation structure.

In below, I will introduce how to implement the navigation in the repository.

react-native-gesture-handler

To use react-navigation, we need to import react-native-gesture-handler. Open index.js or App.js file(top parenet component of react-navigation), and modify it like below.

import 'react-native-gesture-handler';
...

In V4, we needed to use createAppContainer in the last of the module to use Navigations.


import {createAppContainer} from 'react-navigation';
...
export default createAppContainer(
  ...
);

In V5, we need to use NavigationContainer in the last of the module like V4.


import React from 'react';
import {NavigationContainer} from '@react-navigation/native';
...
export default () => {
  return (
    <NavigationContainer>
      ...
    </NavigationContainer>
  );
};

Did you feel already what different is? The previous react-navigation is like a configuration. V5 is like using React Components.

Auth process

In V4, react-navigation provides createSwitchNavigator to process the Auth parts.


import {createSwitchNavigator, createAppContainer} from 'react-navigation';
...
import AuthLoading from './AuthLoading';
import Auth from './Auth';
...
export default createAppContainer(
  createSwitchNavigator(
    {
      AuthLoading,
      Auth,
      MainNavi,
    },
    {
      initialRouteName: 'AuthLoading',
    },
  ),
);

We can use createSwitchNavigator like above. AuthLoading component checks the user already logged in or not, If not logged in, show Auth screen, If logged in, show MainNavi screen.

In V5, createSwitchNavigator does not exist, so If you want to implement Auth process like above, you should make it on the navigation.


import React, {useContext} from 'react';
import {NavigationContainer} from '@react-navigation/native';
...
import {UserContext} from '~/Context/User';
...
export default () => {
  const {userInfo} = useContext<IUserContext>(UserContext);

  return (
    <NavigationContainer>
      {userInfo ? <MainNavi /> : <LoginStackNavi />}
    </NavigationContainer>
  );
};

In V5, createSwitchNavigator is gone, but V5 is like React components unlike V4. So we can implement that Navigation component checks the login status and displays the screen depends on the status.

You can see the details about this in the official site, too.

How to use Stack Navigation

In react-navigation V4, you used Stack Navigation like below.


...
import {createStackNavigator} from 'react-navigation-stack';
...
const MainNavi = createStackNavigator({
  MainTab: {
    screen: MainTab,
    navigationOptions: ({navigation}) => ({
      header: null,
    }),
  },
  FullDetail,
});
...

In react-navigation V5, you can use Stack Navigation like below.


...
import {
  createStackNavigator,
} from '@react-navigation/stack';
...
const Stack = createStackNavigator();
...
const LoginStackNavi = () => {
  return (
    <Stack.Navigator
      screenOptions={{
        headerStyle: {
          backgroundColor: '#f4511e',
        },
        headerTintColor: '#fff',
        headerTitleStyle: {
          fontWeight: 'bold',
        },
      }}>
      <Stack.Screen
        name="SignIn"
        component={SignIn}
        options={{
          headerShown: false,
        }}
      />
      <Stack.Screen
        name="SignUp"
        component={SignUp}
        options={{headerBackTitleVisible: false}}
      />
      <Stack.Screen name="ResetPassword" component={ResetPassword} />
    </Stack.Navigator>
  );
};

In react-navigation V5, If you want to apply the properties for the common header, you can use screenOptions in Stack.Navigator.

If you want to set the header properties to the specific screen, you can use options in Stack.Screen.

The example above includes to hide Header Bar by using headerShown in options.


<Stack.Screen
  name="SignIn"
  component={SignIn}
  options={{
    headerShown: false,
  }}
/>

Also, it includes to hide Back Button title by using headerBackTitleVisible in options.


<Stack.Screen
  name="SignUp"
  component={SignUp}
  options={{headerBackTitleVisible: false}}
/>

How to use Drawer Navigation

In react-navigation V5, you can implement Drawer Navigation like below.


...
import {
  createDrawerNavigator,
  DrawerContentScrollView,
  DrawerItemList,
  DrawerItem,
  DrawerContentComponentProps,
  DrawerContentOptions,
  DrawerNavigationProp,
} from '@react-navigation/drawer';
...
const Drawer = createDrawerNavigator();
...
const CustomDrawerContent = (
  props: DrawerContentComponentProps<DrawerContentOptions>,
  logout: () => void,
) => {
  return (
    <DrawerContentScrollView {...props}>
      <DrawerItemList {...props} />
      <DrawerItem label="Logout" onPress={() => logout()} />
    </DrawerContentScrollView>
  );
};
const DrawNavi = () => {
  const {logout} = useContext<IUserContext>(UserContext);

  return (
    <Drawer.Navigator
      drawerContent={props => CustomDrawerContent(props, logout)}>
      <Drawer.Screen name="TabNavi" component={TabNavi} />
      <Drawer.Screen name="MaterialTabNavi" component={MaterialTabNavi} />
      <Drawer.Screen
        name="MaterialTopTabNaviStackNavi"
        component={MaterialTopTabNaviStackNavi}
      />
    </Drawer.Navigator>
  );
};
...

This example is a little bit complicated because of Logout feature. Except for the logout feature, it looks like below.


...
import {
  createDrawerNavigator,
} from '@react-navigation/drawer';
...
const Drawer = createDrawerNavigator();
...
const DrawNavi = () => {
  return (
    <Drawer.Navigator>
      <Drawer.Screen name="TabNavi" component={TabNavi} />
      <Drawer.Screen name="MaterialTabNavi" component={MaterialTabNavi} />
      <Drawer.Screen
        name="MaterialTopTabNaviStackNavi"
        component={MaterialTopTabNaviStackNavi}
      />
    </Drawer.Navigator>
  );
};
...

It is very similar to Stack Navigation. The example above is to add Logout menu to Drawer Navigation and press it to logout, so it became complicated.

How to use Bottom Tab Navigation

In react-navigation V5, you can implement Bottom Tab Navigation.


...
import {createBottomTabNavigator} from '@react-navigation/bottom-tabs';
import Icon from 'react-native-vector-icons/MaterialIcons';
...
const Tab = createBottomTabNavigator();
...
const TabNavi = () => {
  return (
    <Tab.Navigator>
      <Tab.Screen
        name="TabFirstStackNavi"
        component={TabFirstStackNavi}
        options={{
          tabBarLabel: 'Frist',
          tabBarIcon: ({color}) => <Icon name="home" color={color} size={26} />,
        }}
      />
      <Tab.Screen
        name="TabSecond"
        component={TabSecond}
        options={{
          tabBarLabel: 'Second',
          tabBarIcon: ({color}) => (
            <Icon name="people" color={color} size={26} />
          ),
        }}
      />
      <Tab.Screen
        name="TabThird"
        component={TabThird}
        options={{
          tabBarLabel: 'Third',
          tabBarIcon: ({color}) => (
            <Icon name="message" color={color} size={26} />
          ),
        }}
      />
      <Tab.Screen
        name="TabFourth"
        component={TabFourth}
        options={{
          tabBarLabel: 'Fourth',
          tabBarIcon: ({color}) => (
            <Icon name="settings" color={color} size={26} />
          ),
        }}
      />
    </Tab.Navigator>
  );
};
...

In the example above, I used options in Tab.Screen to show Label and Icon.


<Tab.Screen
  ...
  options={{
    tabBarLabel: 'Fourth',
    tabBarIcon: ({color}) => (
      <Icon name="settings" color={color} size={26} />
    ),
  }}
/>

{% include_cached in-feed-ads.html %}

How to use Material Bottom Tab Navigation

In react-navigation V5, you can implement Bottom Tab Navigation.

...
import {createMaterialBottomTabNavigator} from '@react-navigation/material-bottom-tabs';
import Icon from 'react-native-vector-icons/MaterialIcons';
...
const MaterialTab = createMaterialBottomTabNavigator();
...
const MaterialTabNavi = () => {
  return (
    <MaterialTab.Navigator>
      <MaterialTab.Screen
        name="TabFirstStackNavi"
        component={TabFirstStackNavi}
        options={{
          tabBarColor: '#281b39',
          tabBarLabel: 'Frist',
          tabBarIcon: ({color}) => <Icon name="home" color={color} size={26} />,
        }}
      />
      <MaterialTab.Screen
        name="TabSecond"
        component={TabSecond}
        options={{
          tabBarColor: '#0e141d',
          tabBarLabel: 'Second',
          tabBarIcon: ({color}) => (
            <Icon name="people" color={color} size={26} />
          ),
        }}
      />
      <MaterialTab.Screen
        name="TabThird"
        component={TabThird}
        options={{
          tabBarColor: '#E64A19',
          tabBarLabel: 'Third',
          tabBarIcon: ({color}) => (
            <Icon name="message" color={color} size={26} />
          ),
        }}
      />
      <MaterialTab.Screen
        name="TabFourth"
        component={TabFourth}
        options={{
          tabBarColor: '#524365',
          tabBarLabel: 'Fourth',
          tabBarIcon: ({color}) => (
            <Icon name="settings" color={color} size={26} />
          ),
        }}
      />
    </MaterialTab.Navigator>
  );
};

You should set Tab Bar icon to use Material Bottom Tab Navigation.


<MaterialTab.Screen
  ...
  options={{
    ...
    tabBarIcon: ({color}) => (
      <Icon name="settings" color={color} size={26} />
    ),
  }}
/>

Also, you can set tabBarColor to change the tab bar color by touching the tab.


<MaterialTab.Screen
  ...
  options={{
    ...
    tabBarColor: '#524365',
    ...
  }}
/>

tabBarIcon option is the required option, but tabBarColor is you can implement only you need.

How to useMaterial Top Tab Navigation

In react-navigation V5, you can implement Material Top Tab Navigation like below.


...
import {createMaterialTopTabNavigator} from '@react-navigation/material-top-tabs';
...
const MaterailTopTab = createMaterialTopTabNavigator();
...
const MaterialTopTabNavi = () => {
  return (
    <MaterailTopTab.Navigator>
      <MaterailTopTab.Screen name="TabFirst" component={TabFirst} />
      <MaterailTopTab.Screen name="TabSecond" component={TabSecond} />
      <MaterailTopTab.Screen name="TabThird" component={TabThird} />
      <MaterailTopTab.Screen name="TabFourth" component={TabFourth} />
    </MaterailTopTab.Navigator>
  );
};
...
const MaterialTopTabNaviStackNavi = ({navigation}: DrawerProp) => {
  return (
    <Stack.Navigator
      screenOptions={{
        headerLeft: (props: StackHeaderLeftButtonProps) => (
          <IconButton
            iconName="menu"
            onPress={() => navigation.openDrawer()}
            color="black"
          />
        ),
      }}>
      <Stack.Screen name="MaterialTopTabNavi" component={MaterialTopTabNavi} />
    </Stack.Navigator>
  );
};
...

Simply, We can use Material Top Tab Navigation like below.


const MaterialTopTabNavi = () => {
  return (
    <MaterailTopTab.Navigator>
      <MaterailTopTab.Screen name="TabFirst" component={TabFirst} />
      <MaterailTopTab.Screen name="TabSecond" component={TabSecond} />
      <MaterailTopTab.Screen name="TabThird" component={TabThird} />
      <MaterailTopTab.Screen name="TabFourth" component={TabFourth} />
    </MaterailTopTab.Navigator>
  );
};

However, the above is not shown up perfectly in Notch Design.

material top navigation issue in notch design

To solve this problem, I recommend you wrap it by Stack Navigation like below.


...
const MaterialTopTabNavi = () => {
  return (
    <MaterailTopTab.Navigator>
      <MaterailTopTab.Screen name="TabFirst" component={TabFirst} />
      <MaterailTopTab.Screen name="TabSecond" component={TabSecond} />
      <MaterailTopTab.Screen name="TabThird" component={TabThird} />
      <MaterailTopTab.Screen name="TabFourth" component={TabFourth} />
    </MaterailTopTab.Navigator>
  );
};
...
const MaterialTopTabNaviStackNavi = ({navigation}: DrawerProp) => {
  return (
    <Stack.Navigator
      screenOptions={{
        headerLeft: (props: StackHeaderLeftButtonProps) => (
          <IconButton
            iconName="menu"
            onPress={() => navigation.openDrawer()}
            color="black"
          />
        ),
      }}>
      <Stack.Screen name="MaterialTopTabNavi" component={MaterialTopTabNavi} />
    </Stack.Navigator>
  );
};
...

If you warp with Stack Navigation, you can see the result like below.

notch design에서 material top navigation의 문제점 해결

Drawer Open

If you only use Drawer Navigation, you can see the result like below.

React Navigation V5 drawer Navigation

[picture: reactnavigation.org Official site]

To open Drawer navigaiton, you need to swipe left to right.

However, we implement it by adding a menu icon on the left of the Navigation Header and touching it to show Drawer Navigation, normally.

So, I’ve implemented it by adding the menu icon to the left of the navigation header and calling openDrawer function if it is touched.


...
import {
  ...
  DrawerNavigationProp,
} from '@react-navigation/drawer';
...
type TypeDrawerProp = DrawerNavigationProp<
  {
    TabNavi: undefined;
    MaterialTabNavi: undefined;
    MaterialTopTabNaviStackNavi: undefined;
    Logout: undefined;
  },
  'TabNavi'
>;
interface DrawerProp {
  navigation: TypeDrawerProp;
}
...
const MaterialTopTabNaviStackNavi = ({navigation}: DrawerProp) => {
  return (
    <Stack.Navigator
      screenOptions={{
        headerLeft: (props: StackHeaderLeftButtonProps) => (
          <IconButton
            iconName="menu"
            onPress={() => navigation.openDrawer()}
            color="black"
          />
        ),
      }}>
      <Stack.Screen name="MaterialTopTabNavi" component={MaterialTopTabNavi} />
    </Stack.Navigator>
  );
};

DrawerActions

In the example above, if the screen is under Drawer Navigation directly, you can open the Drawer using the navigation Props. However, like below, if the screen is not under Drawer Navigation directly, you can not open and close the Drawer navigation.


...
const MainTabs = () => {
  return (
    <BottomTab.Navigator tabBarOptions={{activeTintColor: 'black'}}>
      <BottomTab.Screen name="MyFeed" component={MyFeed}/>
      <BottomTab.Screen name="Feeds" component={Feeds}/>
      <BottomTab.Screen name="Upload" component={Upload}/>
      <BottomTab.Screen name="Notification" component={Notification}/>
      <BottomTab.Screen name="Profile" component={Profile}/>
    </BottomTab.Navigator>
  );
};
...
const MainNavigator = () => {
  return (
    <Drawer.Navigator
      drawerPosition="right"
      drawerType="slide"
      drawerContent={props => <CustomDrawer props={props} />}>
      <Drawer.Screen name="MainTabs" component={MainTabs} />
    </Drawer.Navigator>
  );
};

Because, The screen can only get the parenet Navigation Props. In the example above, the parent of Profile screen in MainTab is Bottom Tab navigation not Drawer Navigation. In this case, we can not use navigation.openDrawer().

In this case, we can use DrawerActions.

If the screen is not directly under Drawer Navigation, but is the children of Drawer Navigation, You can open the Drawer like below.


...
import {DrawerActions} from '@react-navigation/native';
...
type NavigationProp = StackNavigationProp<ProfileTabParamList, 'Profile'>;
interface Props {
  navigation: NavigationProp;
}

const Profile = ({navigation}: Props) => {
  return (
    <ScrollView>
      ...
      <IconButton
        iconName="menu"
        onPress={() => navigation.dispatch(DrawerActions.openDrawer())}
      />
      ...
    </ScrollView>
  );
});

export default Profile;

You can open the Drawer Navigation by using navigation.dispatch(DrawerActions.openDrawer()). You can also use the functions below.

navigation.dispatch(DrawerActions.openDrawer());
navigation.dispatch(DrawerActions.closeDrawer());
navigation.dispatch(DrawerActions.toggleDrawer());

setParams / getParams

In react-navigation V4, you used the code below to exchange Data between the screens.

...
navigation.setParams({
  showSearch: false,
});
...
const showSearch = navigation.getParam('showSearch');

In react-navigation V5, you can use setParams, but you can not use getParams.

navigation.setParams({
  showSearch: false,
});

In react-navigation V5, you can use route.params instead of getParams.

...
import { RouteProp } from '@react-navigation/native';
...

type ProfileScreenRouteProp = RouteProp<MovieNaviParamList, 'MovieDetail'>;
interface Props {
  route: ProfileScreenRouteProp;
}

const MovieDetail = ({ route }: Props) => {
  const { id } = route.params;
  ...
});

export default MovieDetail;

It looks a little bit complicated because of Typescript.

const MovieDetail = ({ route, navigation }) => {
  const { id } = route.params;
  ...
});

It looks like above. You can use route Props that comes with navigation Props.

Typescript

Let’s see how to use Typescript in react-navigation v5.

To navigate the screen, you use the source code with navigation Props like below.

const Feeds = ({navigation}: Props) => {
  ...
  navigation.navigate('FeedListOnly');
  ...
});

For this, you should write Typescript like below.

...
import {StackNavigationProp} from '@react-navigation/stack';
...
// Stack Navigation
type NavigationProp = StackNavigationProp<FeedsTabParamList, 'Feeds'>;
interface Props {
  navigation: NavigationProp;
}

const Feeds = ({navigation}: Props) => {
  ...
  navigation.navigate('FeedListOnly');
  ...
});

In above, FeedsTabParamList is a common Type, so make @types/index.d.ts file and modify it like below.

type FeedsTabParamList = {
  Feeds: undefined;
  FeedListOnly: undefined;
};

This Stack Navigation has Feeds and FeedListOnly screens. In this case, it doesn’t have the parameters to navigate the screen, so set undefined.

If the screen need the parameters,

type FeedsTabParamList = {
  Feeds: undefined;
  FeedListOnly: {
    id: number;
  };
};

Define the type like above,

const Feeds = ({navigation}: Props) => {
  ...
  navigation.navigate('FeedListOnly', {
    id: 1,
  });
  ...
});

And navigate the screen with the parameter. It looks very complicated but it is for checking types correctly.

In above, I introduced Stack Navigation, and you can also use each Navigation Props like below.

import {BottomTabNavigationProp} from '@react-navigation/bottom-tabs';
import {DrawerNavigationProp} from '@react-navigation/drawer';
import {MaterialBottomTabNavigationProp} from '@react-navigation/material-bottom-tabs';
import {MaterialTopTabBarProps} from '@react-navigation/material-top-tabs';

The usage is the same as Stack Navigation.

type NavigationProp = BottomTabNavigationProp<FeedsTabParamList, 'Feeds'>;
type NavigationProp = DrawerNavigationProp<FeedsTabParamList, 'Feeds'>;
type NavigationProp = MaterialBottomTabNavigationProp<FeedsTabParamList, 'Feeds'>;
type NavigationProp = MaterialTopTabBarProps<FeedsTabParamList, 'Feeds'>;

route

In above, you can use navigation.setParams to send tha data between the screens. At this time, you use route.params.

To use route Props, you use Typescript like below.

...
import { RouteProp } from '@react-navigation/native';
...
type ProfileScreenRouteProp = RouteProp<MovieNaviParamList, 'MovieDetail'>;
interface Props {
  route: ProfileScreenRouteProp;
}

const MovieDetail = ({ route }: Props) => {
  const { id } = route.params;
  ...
});

export default MovieDetail;

In here, MovieNaviParamList is also used for many screens, so make @types/index.d.ts file and modify it like below.

type MovieNaviParamList = {
  MovieHome: undefined;
  MovieDetail: {
    id: number;
  };
};

Completed

We’ve seen how to install react-navigation V5 and how to implement it. I was surprised by the differences between V4 and V5, but V5 looks like React Components, so I think it is more friendly to React development.

I think all previous features can be implemented. In the react-navigation V5 example, I’ve implemented the previous example of react-navigation V4. If you want to use react-navigation V5, see the example below first!

Reference

Posts