Search code examples
react-nativereact-navigationreact-navigation-stackreact-navigation-v5react-navigation-drawer

React Navigation 5 How to Map Drawer Stack Screens?


I'm trying to do a dynamic Drawer, I have my StackItens (routes) settle like:

import React from 'react';
import Home from '../screens/home/Index';
import ATR from '../screens/atr/Index';

const stackNavigItems = [
  {
    group: 'Home',
    name: 'Home',
    component: Home,
    icon: 'home',
    iconType: 'FontAwesome',
    label: 'Principal',
    hideMenu: false,
    screenComponent: props => {
      return <Home {...props} />;
    },
  },
  {
    group: 'Home',
    name: 'ATR',
    component: ATR,
    icon: 'book',
    iconType: 'Ionicons',
    label: 'ATR',
    hideMenu: false,
    screenComponent: props => {
      return <ATR {...props} />;
    },
  },
];

export {stackNavigItems};

And In my Drawer Content I map these items and works fine. But when I map the same items inside the Navigator using this code:

<PaperProvider theme={theme}>
  <AuthContext.Provider value={authContext}>
    <NavigationContainer theme={theme}>
      <Drawer.Navigator drawerContent={props => <DrawerContent {...props} />}>
    {stackNavigItems.map(r => {
      return (
        <Drawer.Screen
          key={r.name}
          name={r.name}
          component={({navigation}) => (
            <Stack.Navigator
              initialRouteName="Home"
              headerMode="screen"
              screenOptions={screenOptions}>
              <Stack.Screen
                name={r.name}
                component={props => <r.screenComponent {...props} />}
                options={{
                  title: r.label,
                  headerLeft: () => (
                    <Icon.Button
                      name="ios-menu"
                      size={IconStyles.size}
                      backgroundColor={AppStyles.color.main}
                      onPress={() => {
                        navigation.openDrawer();
                      }}
                    />
                  ),
                }}
              />
            </Stack.Navigator>
          )}
        />
      );
    })}
  </Drawer.Navigator>
    </NavigationContainer>
  </AuthContext.Provider>
</PaperProvider>

The app always show me this warning and the header double the size:

enter image description here

I tried so many ways passing a function "()" before the component and nothing works. If I disable the warning, the app works without show the warning but sometimes the header double the size while using like in the picture.


Solution

  • You can avoid using inline functions by creating an extra component and passing that normally and passing an id of the navigation item via initialParams to the extra component:

    const StackNavigator = ({route}) => {
      const navigationItem = stackNavigItems[route.params?.navigationItemId];
      return (
        <Stack.Navigator initialRouteName="Home" headerMode="screen">
          <Stack.Screen
            name={navigationItem.name}
            component={navigationItem.screenComponent}
            options={{
              title: navigationItem.label,
              headerLeft: () => null,
            }}
          />
        </Stack.Navigator>
      );
    };
    
    function App() {
      return (
        <NavigationContainer>
          <Drawer.Navigator drawerContent={(props) => null}>
            {stackNavigItems.map((r) => {
              return (
                <Drawer.Screen
                  key={r.name}
                  name={r.name}
                  component={StackNavigator}
                  initialParams={{navigationItemId: r.id}}
                />
              );
            })}
          </Drawer.Navigator>
        </NavigationContainer>
      );
    }
    

    So for this approach you do need to add an id prop to the objects inside stackNavigItems:

    const stackNavigItems = [
      {
        id: 0,
        group: 'Home',
        name: 'Home',
        component: Home,
        icon: 'home',
        iconType: 'FontAwesome',
        label: 'Principal',
        hideMenu: false,
        screenComponent: (props) => {
          return <Home {...props} />;
        },
      },
      {
        id: 1,
        group: 'Home',
        name: 'ATR',
        component: ATR,
        icon: 'book',
        iconType: 'Ionicons',
        label: 'ATR',
        hideMenu: false,
        screenComponent: (props) => {
          return <ATR {...props} />;
        },
      },
    ];
    

    I've left out some unrelated code, to make this example more readable.