rodolfo
rodolfo

Reputation: 65

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.

Upvotes: 0

Views: 1651

Answers (2)

farland
farland

Reputation: 131

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>
  );
}

Upvotes: 0

bas
bas

Reputation: 15722

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.

Upvotes: 1

Related Questions