3

I've seen other people with the same issue but I can't get my code to work. After upgrading from RN 5 to RN 6, I get the error below.

Found screens with the same name nested inside one another. Check:

AppSideDrawerScreen > HomeSide > HomeBottom, AppSideDrawerScreen > HomeSide > HomeBottom > HomeBottom

Everything worked fine with RN5, but my app crashes since the upgrade. I can access the Home screen from the drawer menu and also bottomTab.

Navigation.js:

import { Entypo } from "@expo/vector-icons";
import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";
import { createDrawerNavigator } from "@react-navigation/drawer";
import { NavigationContainer, useTheme } from "@react-navigation/native";
import { createStackNavigator } from "@react-navigation/stack";
import React, { useCallback, useMemo, useState } from "react";
import { TouchableOpacity } from "react-native";
import FlashMessage from "react-native-flash-message";

import AddGroup from "../screens/AddGroup";
import GroupList from "../screens/GroupList";
import Groups from "../screens/Groups";
import History from "../screens/History";
import Home from "../screens/Home";
import Loading from "../screens/Loading";
import Settings from "../screens/Settings";
import { MyDarkTheme, MyLightTheme } from "../styles/themes";
import { PreferencesContext } from "../util/PreferencesContext";

// Header style to be used on each (normal) screen
const screenOptionWithHeaderStyle = {
  
  presentation: "modal" // For new SDK 46

};

// Header style to be used on modal screens with transparent background and no header
const modalOptionStyle = {
  headerShown: false,
  cardStyle: { backgroundColor: "transparent" },
  cardOverlayEnabled: true,
  cardStyleInterpolator: ({ current: { progress } }) => ({
    cardStyle: {
      opacity: progress.interpolate({
        inputRange: [0, 0.5, 0.9, 1],
        outputRange: [0, 0.25, 0.7, 1],
      }),
    },
    overlayStyle: {
      opacity: progress.interpolate({
        inputRange: [0, 1],
        outputRange: [0, 0.8],
        extrapolate: "clamp",
      }),
    },
  }),
};

/**
 * Component: OpenDrawerIcon
 * Icon on left side to open Drawer menu. Located in headerLeft
 * @param {*} navigation  Navigation object which can then be passed to other functions and components to navigate
 */
const OpenDrawerIcon = ({ navigation }) => {
  return {
    headerLeft: () => (
      <TouchableOpacity onPress={() => navigation.openDrawer()}>
        <Entypo
          name="menu"
          size={32}
          style={{ padding: 5 }}
        />
      </TouchableOpacity>
    ),
  };
};

const HomeStack = createStackNavigator();
/**
 * Component: HomeStackScreen
 * Home screen container
 * @param {*} route  Route information used to retrieve current component name
 */
const HomeStackScreen = ({ route }) => (
  <HomeStack.Navigator screenOptions={screenOptionWithHeaderStyle}>
    <HomeStack.Screen
      name={route.name}
      component={Home}
      options={OpenDrawerIcon}
    />
  </HomeStack.Navigator>
);

const HistoryStack = createStackNavigator();
/**
 * Component: HistoryStackScreen
 * History screen container
 * @param {*} route  Route information used to retrieve current component name
 */
const HistoryStackScreen = ({ route }) => (
  <HistoryStack.Navigator screenOptions={screenOptionWithHeaderStyle}>
    <HistoryStack.Screen
      name={route.name}
      component={History}
      options={OpenDrawerIcon}
    />
  </HistoryStack.Navigator>
);

const GroupsStack = createStackNavigator();
/**
 * Component: GroupsStackScreen
 * Groups screen container
 * @param {*} route  Route information used to retrieve current component name
 */
const GroupsStackScreen = ({ route }) => (
  <GroupsStack.Navigator screenOptions={screenOptionWithHeaderStyle}>
    <GroupsStack.Screen
      name={route.name}
      component={Groups}
      options={OpenDrawerIcon}
    />
  </GroupsStack.Navigator>
);

const SettingsStack = createStackNavigator();
/**
 * Component: SettingsStackScreen
 * Settings screen container
 * @param {*} route  Route information used to retrieve current component name
 */
const SettingsStackScreen = ({ route }) => (
  <SettingsStack.Navigator screenOptions={screenOptionWithHeaderStyle}>
    <SettingsStack.Screen
      name={route.name}
      component={Settings}
      options={OpenDrawerIcon}
    />
  </SettingsStack.Navigator>
);

const AppBottomTabs = createBottomTabNavigator();
/**
 * Component: BottomTabsScreen
 * Bottom tab elements containing the different screens containers, also accessible from Drawer menu
 */
const BottomTabsScreen = () => {

  return (
    <AppBottomTabs.Navigator>
      <AppBottomTabs.Screen
        name="HomeBottom"
        component={HomeStackScreen}
        options={{
          tabBarIcon: (props) => (
            <Entypo name="home" size={props.size} color={props.color} />
          ),
        }}
      />
      <AppBottomTabs.Screen
        name="History"
        component={HistoryStackScreen}
        options={{
          tabBarIcon: (props) => (
            <Entypo name="list" size={props.size} color={props.color} />
          ),
        }}
      />
      <AppBottomTabs.Screen
        name="Groups"
        component={GroupsStackScreen}
        options={{
          tabBarIcon: (props) => (
            <Entypo name="folder" size={props.size} color={props.color} />
          ),
        }}
      />
    </AppBottomTabs.Navigator>
  );
};

const AppSideDrawer = createDrawerNavigator();
/**
 * Component: AppSideDrawerScreen
 * Drawer element containing access to all screens in the app
 */
const AppSideDrawerScreen = () => {
  return (
    <AppSideDrawer.Navigator screenOptions={{ headerShown: false }}>
      <AppSideDrawer.Screen name="HomeSide" component={BottomTabsScreen} />

      <AppSideDrawer.Screen name="History" component={HistoryStackScreen} />

      <AppSideDrawer.Screen name="Groups" component={GroupsStackScreen} />

      <AppSideDrawer.Screen name="Settings" component={SettingsStackScreen} />
    </AppSideDrawer.Navigator>
  );
};

// Root stack container
const RootStack = createStackNavigator();
const RootStackScreen = () => {
  const [isLoading] = React.useState(false);

  return (
    <RootStack.Navigator
      screenOptions={screenOptionWithHeaderStyle}
    >
      {isLoading ? (
        <RootStack.Screen name="Loading" component={Loading} />
      ) : (
        <RootStack.Screen
          name="AppSideDrawerScreen"
          component={AppSideDrawerScreen}
          options={{ headerShown: false }}
        />
      )}
      <RootStack.Screen
        name="AddGroup"
        component={AddGroup}
        options={modalOptionStyle}
      />

      <RootStack.Screen
        name="GroupList"
        component={GroupList}
        options={({ navigation }) => ({
          animationEnabled: true,
          headerTitle: "Select an existing group",
          headerLeft: () => (
            <TouchableOpacity onPress={() => navigation.goBack()}>
              <Entypo
                name="back"
                size={32}
                color="black"
                style={{ padding: 5 }}
              />
            </TouchableOpacity>
          ),
        })}
      />
    </RootStack.Navigator>
  );
};

export default () => {
  // Default theme is dark
  const [isThemeDark, setIsThemeDark] = useState(true);
  const theme = isThemeDark ? MyDarkTheme : MyLightTheme;

  const toggleTheme = useCallback(() => {
    return setIsThemeDark(!isThemeDark);
  }, [isThemeDark]);

  // To remember preferences accross the app
  const preferences = useMemo(
    () => ({
      toggleTheme,
      isThemeDark,
    }),
    [toggleTheme, isThemeDark]
  );

  return (
    <PreferencesContext.Provider value={preferences}>
      <NavigationContainer theme={theme}>
        <RootStackScreen />
        <FlashMessage position="center" floating />
      </NavigationContainer>
    </PreferencesContext.Provider>
  );
};

I already renamed one Home to HomeSide and another one to HomeBottom but that didn't help. I suspect it could be because of this: HomeStack.Screen name={route.name} ?

I tried to make a map of my navigators and I think it looks like that:

RootStack.Navigator
    RootStack.Screen name="Loading"
    RootStack.Screen name="AppSideDrawerScreen"
        AppSideDrawer.Navigator
            AppSideDrawer.Screen name="HomeSide"
                AppBottomTabs.Navigator
                    AppBottomTabs.Screen name="HomeBottom"
                        HomeStack.Navigator
                            HomeStack.Screen name={route.name}
                    AppBottomTabs.Screen name="History"
                    AppBottomTabs.Screen name="Groups"
            AppSideDrawer.Screen name="History"
                HistoryStack.Navigator
                    HistoryStack.Screen name={route.name}
            AppSideDrawer.Screen name="Groups"
                GroupsStack.Navigator
                    GroupsStack.Screen name={route.name}
            AppSideDrawer.Screen name="Settings"
                SettingsStack.Navigator
                    SettingsStack.Screen name={route.name}
    RootStack.Screen name="AddGroup"
    RootStack.Screen name="GroupList"
remyremy
  • 3,548
  • 3
  • 39
  • 56
  • There are a few ways to solve this issue. One way would be to use the unique ID of each screen to ensure that each screen has a unique name. Another way would be to use a different name for each screen. For example, you could use the name of the screen as the key for the drawer navigator, and then use the unique ID of the screen as the key for the stack navigator. – Mohamed Elgazar Oct 16 '22 at 15:04
  • Is it in `name={route.name}` I need to add a unique ID? Because I already tried to change to `HomeSide` and `HomeBottom` in `AppSideDrawer.Screen name="HomeSide"` and `AppBottomTabs.Screen name="HomeBottom"`. I'm not sure where I would have to do that. thanks – remyremy Oct 16 '22 at 15:07
  • You could try using the unique ID of the screen as the key for the drawer navigator, and then use the unique ID of the screen as the key for the stack navigator. – Mohamed Elgazar Oct 16 '22 at 15:11
  • I've tried to name my screen with [getId](https://reactnavigation.org/docs/screen/#getid) but that doesn't seem to work. Would you be able to provide a small sample code? Cheers! – remyremy Oct 16 '22 at 20:13
  • You should explicitly name your screens bb. You’re reusing the same component and pulling its name from route.name, which you’re also reusing in multiple navigators. Just make each screen in a navigator have a prefix unique to the navigator. – invzbl3 Oct 29 '22 at 08:09

1 Answers1

0

I ended up calling the screens with different names, and then added a title to use when displaying the screen name to the user.

I also added a switch inside OpenDrawerIcon as the logic was a bit more complex there:

const OpenDrawerIcon = (route, navigation) => {
  let titleName = "";
  switch (route.name) {
    case "HomeStack":
      titleName = "Home";
      break;
    case "HistoryStack":
      titleName = "History";
      break;
    case "GroupsStack":
      titleName = "Groups";
      break;
    case "SettingsStack":
      titleName = "Settings";
      break;
    default:
      titleName = "DefaultTitle";
  }

  return {
    headerLeft: () => (
      <TouchableOpacity onPress={() => navigation.openDrawer()}>
        <Entypo
          name="menu"
          size={32}
          style={{ padding: 5 }}
        />
      </TouchableOpacity>
    ),
    title: titleName,
  };
};

And here are my screens:

const HomeStackScreen = () => {
  return (
    <HomeStack.Navigator screenOptions={screenOptionWithHeaderStyle}>
      <HomeStack.Screen
        name="HomeStack"
        component={Home}
        options={({ route, navigation }) => OpenDrawerIcon(route, navigation)}
        // options={OpenDrawerIcon} // Old way
      />
    </HomeStack.Navigator>
  );
};
remyremy
  • 3,548
  • 3
  • 39
  • 56