React Navigation 5 How to Map Drawer Stack Screens?

1.5k views Asked by At

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.

2

There are 2 answers

3
bas On BEST ANSWER

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.

0
farland On

component={r.ScreenComponent}.

In my case, I use Stack.Screen, and it works ok.

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} />;
    },
  },
];

...

export default function App() {
  return (
    <PaperProvider>
      <NavigationContainer>
        <Stack.Navigator initialRouteName="Home">
          {stackNavigItems.map((item, index) => {
            return (
              <Stack.Screen key={index} name={item.name} component={item.ScreenComponent} options={{title:item.title}}/>
            )
          })}
        </Stack.Navigator>
      </NavigationContainer>
    </PaperProvider>
  );
}