2

I am in the process of implementing drawer navigation with nested tab navigation within my app.

At present I have two issues that I cannot work out so far and am looking for pointers. I am using React Navigation v6.

  1. Upon clicking any of my tab navigator links I notice that my screen title is always set to "Home". Can this be updated to the current selected tab?

  2. The last clicked tab appears to keep its state when using the drawer links, the journey would look like

  • Start on home page (Renders home screen fine)
  • click userprofile tab (Renders user profile page fine)
  • then click "Book New Class" from within the Drawer, (Renders New Class page fine)
  • then click "Home" from within the Drawer (This renders the user profile screen)

I have created a snack that will hopefully show the issues I'm facing https://snack.expo.dev/@richlewis14/drawer-and-tab-navigation

DrawerNavigator.js

import React, {useContext} from 'react';
import CustomDrawer from '../components/CustomDrawer.js';
import {BookNewEvent} from '../screens/events/BookNewEvent.js';
import {UserProfile} from '../screens/profile/UserProfile.js';

import BottomTabNavigator from '../navigation/BottomTabNavigator.js';
import {createDrawerNavigator} from '@react-navigation/drawer';
import Ionicons from 'react-native-vector-icons/Ionicons';

const Drawer = createDrawerNavigator();

const DrawerNavigator = ({navigation}) => {
  return (
    <Drawer.Navigator
      drawerContent={props => <CustomDrawer {...props} />}
      screenOptions={{
        drawerActiveBackgroundColor: '#2B6EB5',
        drawerActiveTintColor: '#fff',
        drawerInactiveTintColor: '#333',
        drawerLabelStyle: {marginLeft: -25, fontSize: 15},
      }}>
      <Drawer.Screen
        name="Home"
        component={BottomTabNavigator}
        options={{
          title: 'Home',
          drawerIcon: ({color}) => <Ionicons name="home" size={22} color={color} />,
        }}
      />
      <Drawer.Screen
        name="BookNewEvent"
        component={BookNewEvent}
        options={{
          title: 'Book Class',
          drawerIcon: ({color}) => <Ionicons name="calendar-outline" size={22} color={color} />,
      }}
    />
    </Drawer.Navigator>
  );
};
export default DrawerNavigator;

BottomTabNavigator.js

import React, {useContext} from 'react';

import {UserLandingPage} from '../screens/landing/UserLandingPage.js';
import {BookNewEvent} from '../screens/events/BookNewEvent.js';
import {UserProfile} from '../screens/profile/UserProfile.js';


import {createBottomTabNavigator} from '@react-navigation/bottom-tabs';
import {createNativeStackNavigator} from '@react-navigation/native-stack';
import Ionicons from 'react-native-vector-icons/Ionicons';

const Tab = createBottomTabNavigator();
const Stack = createNativeStackNavigator();

const UserHomeStack = () => {
  return (
    <Stack.Navigator>
      <Stack.Screen
        name="Home"
        component={UserLandingPage}
        options={{headerShown: false}}
      />
    </Stack.Navigator>
  );
};


const BottomTabNavigator = ({navigation}) => {
  return (
    <Tab.Navigator
      screenOptions={{
      headerShown: false,
      tabBarShowLabel: false,
    }}>
      <Tab.Screen
        name="Home2"
        component={UserHomeStack}
        options={{
          tabBarIcon: () => <Ionicons name="home" size={22} />,
        }}
      />
      <Tab.Screen
        name="UserBookNewEvent"
        component={BookNewEvent}
        options={{
          tabBarIcon: () => <Ionicons name="add-circle" size={22} />,
        }}
      />
      <Tab.Screen
        name="UserProfilePage"
        component={UserProfile}
        options={{
          tabBarIcon: () => <Ionicons name="person" size={22} color="black" />,
        }}
      />
    </Tab.Navigator>
  );
};
export default BottomTabNavigator;
halfer
  • 19,824
  • 17
  • 99
  • 186
Richlewis
  • 15,070
  • 37
  • 122
  • 283
  • Fixing your first issue is not a problem. Getting the navigation right is not solvable in my experience. You cannot navigate from a DrawNavigator to a screen and select the corresponding tab in the TabNavigator at the same time or the other way around. They both can navigate to the same screen, but either the draw navigator or the tab navigator will vanish. If we let the TabNavigator be a child of the DrawNavigator, as it currently is, then you can navigate via the Tabs but not from the drawer. We can fix this as well, but the tab navigator will not be visible for two of the screens. – David Scholz Feb 21 '22 at 16:23
  • Compare this with [this issue](https://github.com/react-navigation/react-navigation/issues/7728). – David Scholz Feb 21 '22 at 16:25
  • Hi @DavidScholz , the idea here is for the Home link within the drawer to render a bottom tab navigator (the other links in the drawer will not, well at least for now). Would you say the implementation here is the incorrect way then? – Richlewis Feb 21 '22 at 16:33
  • So it is okay, that on the Profile Screen, there is no Tab Navigation? If selected with the drawer of course – David Scholz Feb 21 '22 at 16:33
  • For now yes..from my understanding (which could be wrong), I have attached a tab navigator to the home screen drawer only. One of my issues is that if i select 'Book a class' from the drawer and then go back to the home draw it will render the last clicked tab navigator and not the home screen itself – Richlewis Feb 21 '22 at 16:36
  • Ooh now I see what your issue is. Yes this is solvable, but hacky. – David Scholz Feb 21 '22 at 16:49
  • 1
    Would you say i have misunderstood the implementation guide for nested navigators? or is this a limitation of react-navigation (I would doubt its this one). I'm happy for a fix, but I would like to learn the "correct" way of doing it (if such a thing exists) – Richlewis Feb 21 '22 at 16:53

1 Answers1

2

You are nesting a Tab.Navigator inside a Drawer.Navigator. Compare this with the following code snippet from your snack.

<Drawer.Screen
          name="Home"
          component={BottomTabNavigator}
          options={{
            title: 'Home',
            drawerIcon: ({color}) => <Ionicons name="home" size={22} color={color} />,
          }}
 />

This is not a problem but you need to keep in mind that nesting navigators change certain behaviors of your application, e.g.

Each navigator has its own options​. For example, specifying a title option in a screen nested in a child navigator won't affect the title shown in a parent navigator.

We can fix this by using the code provided in the official documentation of react-native for setting parent screen options based on child navigators state.

Here is what I have done in order to fix your title issue. In BottomTabNavigator.js:

const BottomTabNavigator = ({navigation, route}) => {

function getHeaderTitle(route) {
  const routeName = getFocusedRouteNameFromRoute(route) ?? 'Home';

  switch (routeName) {
    case 'Home2':
      return 'Home';
    case 'UserBookNewEvent':
      return 'Book Class';
    case 'UserProfilePage':
      return 'Profile';
  }
}

React.useLayoutEffect(() => {
    navigation.setOptions({ headerTitle: getHeaderTitle(route) });
  }, [navigation, route]);

...

Notice that our BottomTabNavigator is a child of the Drawer for the Home-Screen, thus it seems that both navigator navigate to the same screen while updating each others navigation states. This is not possible. We can see this, if we navigate via the Drawer to a different screen, then its title is now updated, however, the screen stays the same.

If it is desired to navigate via the Tab.Navigator if the user is on the Home-screen, such that both navigators stay visible, then this is possible. If the user navigates to a screen, different then the Home- screen via the Drawer, then the Tab.Navigator will vanish.

Thanks to the discussion from the comments, this is acceptable. Solving your second issue can be done with a little hack by setting the prop unmountOnBlur to true for the Home-screen provided in the DrawerNavigator as follows.

<Drawer.Screen
          name="Home"
          component={BottomTabNavigator}
          options={{
            title: 'Home',
            unmountOnBlur:true,
            drawerIcon: ({color}) => <Ionicons name="home" size={22} color={color} />,
          }}
 />

Please consider my updated snack for a full working solution.

David Scholz
  • 8,421
  • 12
  • 19
  • 34
  • 1
    thank you so much, great detailed answer too. I actually tried the getHeaderTitle method approach, but used the first option `Using options prop on Screen (recommended):`, this did not work (feel silly now for not trying the second option useLayoutEffect haha) – Richlewis Feb 21 '22 at 17:03
  • 1
    You are very welcome, I hope it helps a bit! – David Scholz Feb 21 '22 at 17:09