Issue with nesting a Drawer Navigator inside a BottomTabs Navigator in React Navigation

33 Views Asked by At

I'm facing a problem with nesting a Drawer Navigator inside a BottomTabs Navigator using React Navigation in my React Native app. I've provided the relevant code below, and despite multiple attempts, I'm encountering an error related to the TOGGLE_DRAWER action not being handled by any navigator.

Custom drawer component MyDrawerContent that I pass to drawerContent prop of Drawer.Navigator

In my ExpensesOverview function I have BottomTabs.Navigator where I call toggleDrawer

function ExpensesOverview() {

    const expensesCtx = useContext(ExpensesContext);

    return (
      <BottomTabs.Navigator
        screenOptions={({navigation}) => ({
          headerStyle: {backgroundColor: GlobalStyles.colors.primary500},
          headerTintColor: 'white',
          tabBarStyle: {backgroundColor: GlobalStyles.colors.primary500},
          tabBarActiveTintColor: GlobalStyles.colors.accent500,
          // headerLeft: () => <CustomHeader />, // Use CustomHeader here
          // headerLeft: () => renderDrawerIcon(navigation),
          headerLeft: () => (
            <IconButton
              icon="menu"
              size={30}
              color="white"
              onPress={() => {
                navigation.dispatch(DrawerActions.toggleDrawer());
              }}
            />
          ),
)}

Then, in my App function I have the NavigationContainer > Stack.Navigator and the ExpensesOverview, ManageExpense(imported component) and lastly the DrawerNavigator. Placing DrawerNavigator last is the only way I get bottom tabs to appear and the Drawer navigator in header left.

If I place Drawer navigator as the first Stack.Screen I can toggle the drawer but the bottom tabs don't appear.

If I place the Stack.Screen Drawer navigator last, the bottom tabs do appear as does the Drawer navigator but when I press on the Drawer I get error:

`ERROR The action 'TOGGLE_DRAWER' was not handled by any navigator.

Is your screen inside a Drawer navigator?`

//Found inside App function
return (
      <>
        <StatusBar style="light" />
        <NavigationContainer>
          <Stack.Navigator
            screenOptions={{
              headerStyle: {backgroundColor: GlobalStyles.colors.primary500},
              headerTintColor: 'white',
            }}>
            {isAuthenticated ? (
              <>
                <Stack.Screen
                  name="ExpensesOverview"
                  component={ExpensesOverview}
                  options={{headerShown: false}}
                />
                <Stack.Screen
                  name="ManageExpense"
                  component={ManageExpense}
                  options={{
                    presentation: 'modal',
                    headerShown: true,
                  }}
                />
                <Stack.Screen
                  name="DrawerNavigator"
                  component={MyDrawer}
                  options={{headerShown: true}}
                />
              </>
            ) : (
              <Stack.Screen
                name="LoginScreen"
                component={LoginScreen}
                options={{headerShown: false}}
              />
            )}
          </Stack.Navigator>
        </NavigationContainer>
      </>
    );

Below is the whole App.jsx file for context:

const Stack = createNativeStackNavigator()
const BottomTabs = createBottomTabNavigator()
const Drawer = createDrawerNavigator();


function MyDrawerContent({ navigation }) {
  return (
    <DrawerContentScrollView>
      <DrawerItem
        label="Recent Expenses"
        onPress={() => {
          navigation.dispatch(DrawerActions.closeDrawer())
          navigation.navigate('Recent')
        }}
      />
    </DrawerContentScrollView>
  )
}

function MyDrawer() {
  return (
    <Drawer.Navigator
      drawerContent={(props) => <MyDrawerContent {...props} />}>
      <Drawer.Screen name="Recent" component={RecentExpenses} />
    </Drawer.Navigator>
  );
}


const renderDrawerIcon = (navigation) => (<IconButton
              icon="menu"
              size={30}
              color="white"
              onPress={() =>
                navigation.dispatch(
                  DrawerActions.toggleDrawer(),
                )
              } // Open the Drawer when the button is pressed
/>
)


function ExpensesOverview() {

    const expensesCtx = useContext(ExpensesContext);

    return (
      <BottomTabs.Navigator
        screenOptions={({navigation}) => ({
          headerStyle: {backgroundColor: GlobalStyles.colors.primary500},
          headerTintColor: 'white',
          tabBarStyle: {backgroundColor: GlobalStyles.colors.primary500},
          tabBarActiveTintColor: GlobalStyles.colors.accent500,
          // headerLeft: () => <CustomHeader />, // Use CustomHeader here
          // headerLeft: () => renderDrawerIcon(navigation),
          headerLeft: () => (
            <IconButton
              icon="menu"
              size={30}
              color="white"
              // onPress={() => navigation.dispatch(DrawerActions.toggleDrawer(MyDrawer))}
              onPress={() => {
                navigation.dispatch(DrawerActions.toggleDrawer());
              }}
            />
          ),
          headerRight: ({tintColor}) => (
            <IconButton
              icon="add"
              size={24}
              color={tintColor}
              onPress={() => {
                navigation.navigate('ManageExpense');
              }}
            />
          ),
        })}>
        <BottomTabs.Screen
          name="RecentExpenses"
          component={RecentExpenses}
          options={{
            title: 'Recent Expenses',
            tabBarLabel: 'Recent',
            tabBarIcon: ({color, size}) => (
              <Ionicons name="hourglass" size={size} color={color} />
            ),
          }}
        />
        <BottomTabs.Screen
          name="Charts"
          component={ChartScreen}
          options={{
            title: 'Charts',
            tabBarLabel: 'Analytics',
            tabBarIcon: ({color, size}) => (
              <Ionicons name="pie-chart-outline" size={size} color={color} />
            ),
          }}
        />
        <BottomTabs.Screen
          name="AllExpenses"
          component={AllExpenses}
          options={{
            title: 'All Expenses',
            tabBarLabel: 'All Expenses',
            tabBarIcon: ({color, size}) => (
              <Ionicons name="calendar" size={size} color={color} />
            ),
          }}
        />
      </BottomTabs.Navigator>
    );
}

function App({ navigation }) {
    const expensesCtx = useContext(ExpensesContext);
    const { isAuthenticated } = expensesCtx;
    const [isFontLoaded, setIsFontLoaded] = useState(false);
    

    const logout = async () => {
        try {
            // Remove the userToken from AsyncStorage
            await AsyncStorage.removeItem('userToken', 'userId');
            // await AsyncStorage.removeItem('userId');
            await expensesCtx.logoutUser()
        } catch (error) {
            console.error('Error logging out:', error);
        }
    };

    const checkAuthentication = async () => {
        try {
            const token = await AsyncStorage.getItem('userToken');
            const userId = await AsyncStorage.getItem('userId');
            // console.log('App.js checkAuth:', userId);
            if (token) {
                await expensesCtx.updateIsAuthenticated();
                await expensesCtx.set_id(userId);
                // console.log('App.js checkAuth:', userId, '_h', token);
                await expensesCtx.setExpenses(userId)

            }
        } catch (error) {
            console.error('Error checking authentication:', error);
        }
    };

    useEffect(() => {
        checkAuthentication();
    }, []);

    return (
      <>
        <StatusBar style="light" />
        <NavigationContainer>
          <Stack.Navigator
            screenOptions={{
              headerStyle: {backgroundColor: GlobalStyles.colors.primary500},
              headerTintColor: 'white',
            }}>
            {isAuthenticated ? (
              <>
                <Stack.Screen
                  name="ExpensesOverview"
                  component={ExpensesOverview}
                  options={{headerShown: false}}
                />
                <Stack.Screen
                  name="ManageExpense"
                  component={ManageExpense}
                  options={{
                    presentation: 'modal',
                    headerShown: true,
                  }}
                />
                <Stack.Screen
                  name="DrawerNavigator"
                  component={MyDrawer}
                  options={{headerShown: true}}
                />
              </>
            ) : (
              <Stack.Screen
                name="LoginScreen"
                component={LoginScreen}
                options={{headerShown: false}}
              />
            )}
          </Stack.Navigator>
        </NavigationContainer>
      </>
    );
}

export default function AppContext() {
    return (
        <ExpensesContextProvider>
            <App />
        </ExpensesContextProvider>
    )
}


0

There are 0 best solutions below