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