I have been using "@react-navigation/native": "^6.0.2" and the app worked well, after then I decided to update all dependencies and use "@react-navigation/native": "7.1.6" but then I keep getting an error message Call Stack RNCSafeAreaView (<anonymous>) ERROR Warning: Error: Couldn't get the navigation state. Is your component inside a navigator?
I really do not know what to do anymore, I have tried and searched for answers but couldn't find one
This is what my App.tsx looks like
import * as SplashScreen from "expo-splash-screen";
import * as SecureStore from "expo-secure-store";
import {useState, useEffect} from "react";
import {
Inter_400Regular,
Inter_500Medium,
Inter_600SemiBold,
Inter_700Bold,
Inter_800ExtraBold,
} from "@expo-google-fonts/inter";
import {NavigationContainer} from "@react-navigation/native";
import Main from "./Main";
import {MainContext} from "./context/MainContext";
import {settingsType} from "./types";
import {
SafeAreaProvider,
SafeAreaView,
useSafeAreaInsets,
} from "react-native-safe-area-context";
import {Appearance, StatusBar, View} from "react-native";
import {TC} from "./constants";
export default function App() {
const [isFontReady, setIsFontReady] = useState<boolean>(false);
const [isUserReady, setIsUserReady] = useState<boolean>(false);
const [user, setUser] = useState(null);
const [settings, setSettings] = useState<settingsType | undefined>(undefined);
const [isLogin, setIsLogin] = useState<boolean>(false);
const [colorScheme, setColorScheme] = useState(Appearance.getColorScheme());
SplashScreen.preventAutoHideAsync();
const [fontsLoaded] = Font.useFonts({
regular: Inter_400Regular,
medium: Inter_500Medium,
"semi-bold": Inter_600SemiBold,
bold: Inter_700Bold,
"extra-bold": Inter_800ExtraBold,
});
useEffect(() => {
async function prepare() {
try {
if (fontsLoaded && isUserReady) {
await SplashScreen.hideAsync();
}
if (fontsLoaded) {
setIsFontReady(true);
}
const user = await SecureStore.getItemAsync("user");
const setting = await SecureStore.getItemAsync("settings");
if (user) {
setUser(JSON.parse(user));
setIsLogin(true);
}
if (!setting) {
const newSetting: settingsType = {
appearance: "system",
passCode: null,
isBiometrics: false,
};
setSettings(newSetting);
await SecureStore.setItemAsync(
"settings",
JSON.stringify({
appearance: "system",
passCode: null,
isBiometrics: false,
})
);
} else {
setSettings(JSON.parse(setting));
}
setIsUserReady(true);
} catch (e) {
console.warn(e);
}
}
prepare();
}, [fontsLoaded, isUserReady]);
useEffect(() => {
const colorListener = Appearance.addChangeListener(({colorScheme}) =>
setColorScheme(colorScheme)
);
return () => {
colorListener.remove();
};
}, []);
if (!isFontReady || !isUserReady) return null;
const theme =
settings?.appearance === "system" && colorScheme
? colorScheme
: settings?.appearance && settings.appearance !== "system"
? settings.appearance
: "light";
return (
<MainContext
settings={settings}
setSettings={setSettings}
colorScheme={colorScheme}
>
<StatusBar
backgroundColor={TC[theme].bg1}
barStyle={theme === "light" ? "dark-content" : "light-content"}
/>
<SafeAreaProvider>
<SafeAreaView
style={{
flex: 1,
backgroundColor: TC[theme].bg1,
}}
>
<NavigationContainer>
<Main getLogin={isLogin} getUser={user} getSettings={settings}/>
</NavigationContainer>
</SafeAreaView>
</SafeAreaProvider>
</MainContext>
);
}
And after which I have the Main.tsx
import {useEffect, useRef, Fragment} from "react";
import {useNavigationState, useNavigation} from "@react-navigation/native";
import {createNativeStackNavigator} from "@react-navigation/native-stack";
import {BackHandler, ToastAndroid,} from "react-native";
import MainNavigation from "./navigation/MainNavigation";
import Profile from "./screens/Profile";
// import AddTransaction from "./screens/AddTransaction";
import Transaction from "./screens/Transaction";
import Settings from "./screens/Settings";
import ChangePassword from "./screens/ChangePassword";
import Login from "./screens/auth/Login";
import {
SignUp,
ForgotPassword,
OnBoarding,
Verification,
AuthPin,
EmailSent,
} from "@/screens/auth";
import Header from "./components/headers/Header";
import {useMainContext} from "./context/MainContext";
import {settingsType, userType} from "@/types";
import {TC} from "@/constants";
import {Airtime, Cable, Data, Electricity} from "./screens/bills";
const Main = ({
getUser,
getLogin,
getSettings,
}: {
getUser: userType | null;
getSettings: settingsType | undefined;
getLogin: boolean;
}) => {
const Stack = createNativeStackNavigator();
const {
isLogin,
setUser,
setIsLogin,
isLocked,
// user,
setSettings,
theme,
settings,
} = useMainContext();
const doublePressInterval = 1000;
const lastBackPressed = useRef(0);
const navigation = useNavigation();
const navigationState = useNavigationState((state) => state);
useEffect(() => {
const backHandler = BackHandler.addEventListener(
"hardwareBackPress",
() => {
const currentTime = Date.now();
const state = navigation.getState();
// Check if the current screen is the root screen
const isRootScreen = state?.routes.length === 1 && state.index === 0;
if (isRootScreen) {
// Check if the time interval between presses is less than the double press interval
if (currentTime - lastBackPressed.current < doublePressInterval) {
// Exit the application
BackHandler.exitApp();
return true;
}
// Show a toast indicating double press is required to exit
ToastAndroid.show("Press back again to exit", ToastAndroid.SHORT);
// Update the lastBackPressed time
lastBackPressed.current = currentTime;
return true;
}
return false;
}
);
return () => backHandler.remove();
}, [navigationState]);
useEffect(() => {
setUser(getUser);
setIsLogin(getLogin);
setSettings(getSettings);
}, [getUser]);
return (
<Stack.Navigator>
{isLogin ? (
<Fragment>
{!settings?.passCode ? (
<Stack.Screen
name="CreatePin"
component={AuthPin}
initialParams={{mode: "create-pin"}}
options={{headerShown: false}}
/>
) : isLocked ? (
<Stack.Screen
name="Locked"
component={AuthPin}
initialParams={{mode: "locked"}}
options={{headerShown: false}}
/>
) : (
<Fragment>
<Stack.Screen
name="main-navigation"
component={MainNavigation}
options={{headerShown: false}}
/>
<Stack.Screen
name="profile"
component={Profile}
options={{headerShown: false}}
/>
{/* <Stack.Screen
name="add-transaction"
component={AddTransaction}
options={{ headerShown: false }}
/> */}
<Stack.Screen
name="transaction"
component={Transaction}
options={{headerShown: false}}
/>
<Stack.Screen
name="settings"
component={Settings}
options={{header: () => <Header text="Settings"/>}}
/>
<Stack.Screen
name="password"
component={ChangePassword}
options={{
header: () => <Header text="Change Password"/>,
}}
/>
<Stack.Screen
name="ChangePin"
component={AuthPin}
options={{headerShown: false}}
initialParams={{mode: "change-pin"}}
/>
<Stack.Screen
name="Airtime"
component={Airtime}
options={{
header: () => (
<Header color={TC[theme].text1} bg={TC[theme].bg1}/>
),
}}
/>
<Stack.Screen
name="Data"
component={Data}
options={{
header: () => (
<Header color={TC[theme].text1} bg={TC[theme].bg1}/>
),
}}
/>
<Stack.Screen
name="Cable"
component={Cable}
options={{
header: () => (
<Header color={TC[theme].text1} bg={TC[theme].bg1}/>
),
}}
/>
<Stack.Screen
name="Electricity"
component={Electricity}
options={{
header: () => (
<Header color={TC[theme].text1} bg={TC[theme].bg1}/>
),
}}
/>
</Fragment>
)}
</Fragment>
) : (
<Fragment>
<Stack.Screen
name="on-boarding"
component={OnBoarding}
options={{headerShown: false}}
/>
<Stack.Screen
name="login"
component={Login}
options={{
header: () => (
<Header
text="Login"
color={TC[theme].text1}
bg={TC[theme].bg1}
/>
),
}}
/>
<Stack.Screen
name="signup"
component={SignUp}
options={{
header: () => (
<Header
text="Sign Up"
color={TC[theme].text1}
bg={TC[theme].bg1}
/>
),
}}
/>
<Stack.Screen
name="verification"
component={Verification}
options={{
header: () => (
<Header
text="Verification"
color={TC[theme].text1}
bg={TC[theme].bg1}
/>
),
}}
/>
<Stack.Screen
name="forgot-password"
component={ForgotPassword}
options={{
header: () => (
<Header
text="Forgot Password"
color={TC[theme].text1}
bg={TC[theme].bg1}
/>
),
}}
/>
<Stack.Screen
name="email-sent"
component={EmailSent}
options={{headerShown: false}}
/>
</Fragment>
)}
</Stack.Navigator>
);
};
export default Main;
And then the MainNavigation.tsx
import {
BottomTabBarProps,
createBottomTabNavigator,
} from "@react-navigation/bottom-tabs";
import {MaterialCommunityIcons} from "@expo/vector-icons";
import {StyleSheet, TouchableOpacity, View} from "react-native";
import {TABS} from "@/constants";
import {icons, ScreenNames} from "@/types";
import {COLORS, TC} from "@/constants";
import {useMainContext} from "@/context/MainContext";
import Home from "@/screens/main-navigation/Home";
import Transactions from "@/screens/main-navigation/Transactions";
import Fund from "@/screens/main-navigation/Fund";
import Profile from "@/screens/Profile";
import Settings from "@/screens/Settings";
import PopAction from "@/components/PopAction";
import {Fragment} from "react";
import {getFocusedRouteNameFromRoute, NavigationContainer} from "@react-navigation/native";
const Tab = createBottomTabNavigator();
export default function MainNavigation({route}: { route: any }) {
const {theme, handleLogout, isLogoutVisible, setIsLogoutVisible} =
useMainContext();
const focusedTab = getFocusedRouteNameFromRoute(route);
const tabBar = ({state, descriptors, navigation}: BottomTabBarProps) => (
<View style={[styles.tabBar, {backgroundColor: TC[theme].tabBar}]}>
{state.routes.map((route, index) => {
const {options} = descriptors[route.key];
const label =
options.tabBarLabel !== undefined
? options.tabBarLabel
: options.title !== undefined
? options.title
: route.name;
const focused = state.index === index;
const Icon =
options.tabBarIcon &&
options.tabBarIcon({focused, color: COLORS.blue100, size: 35});
const screenName = route.name as ScreenNames;
return label === "fund" ? (
<View
key={route.key}
style={[styles.action, {backgroundColor: TC[theme].action}]}
>
<TouchableOpacity
onPress={() => navigation.navigate(screenName)}
style={[
styles.actionBtn,
{backgroundColor: focused ? COLORS.light100 : COLORS.blue100},
]}
>
{Icon}
</TouchableOpacity>
</View>
) : (
<TouchableOpacity
key={route.key}
onPress={() => navigation.navigate(screenName)}
style={{
alignItems: "center",
justifyContent: "center",
height: 50,
}}
>
{Icon}
</TouchableOpacity>
);
})}
</View>
);
return (
<Fragment>
<Tab.Navigator
initialRouteName="Home"
screenOptions={{
tabBarHideOnKeyboard: true,
}}
tabBar={tabBar}
>
{TABS(Home, Transactions, Fund, Profile, Settings).map((tab, index) => (
<Tab.Screen
name={tab.name}
component={tab.component}
options={{
tabBarIcon: ({focused}) => {
return (
<MaterialCommunityIcons
name={
focused ? (tab.focused as icons) : (tab.idle as icons)
}
color={focused ? COLORS.focused : TC[theme].idle}
size={tab.name === "fund" ? 45 : 38}
/>
);
},
headerShown: false,
}}
key={index}
/>
))}
</Tab.Navigator>
{focusedTab === "profile" && (
<PopAction
visible={isLogoutVisible}
onCancel={() => setIsLogoutVisible(false)}
onComplete={handleLogout}
headText="Logout?"
bodyText="Are you sure you want to logout"
/>
)}
</Fragment>
);
}
const styles = StyleSheet.create({
tabBar: {
flexDirection: "row",
alignItems: "flex-end",
justifyContent: "space-between",
paddingHorizontal: 20,
paddingBottom: 10,
position: "absolute",
right: 0,
left: 0,
bottom: 0,
backgroundColor: COLORS.dark25,
borderTopRightRadius: 20,
borderTopLeftRadius: 20,
},
action: {
backgroundColor: COLORS.light100,
height: 50,
width: 100,
marginBottom: 20,
alignItems: "center",
justifyContent: "center",
borderBottomLeftRadius: 50,
borderBottomRightRadius: 50,
},
actionBtn: {
alignItems: "center",
justifyContent: "center",
height: 70,
width: 70,
backgroundColor: COLORS.blue100,
borderRadius: 100,
marginTop: -45,
},
});
I really do not know where the problem is coming from because it was working in the previous version
Here is also my installed packages
{
"name": "salome_trakrr",
"main": "expo/AppEntry.js",
"version": "1.0.0",
"scripts": {
"start": "expo start",
"android": "expo start --android",
"ios": "expo start --ios",
"lint": "expo lint"
},
"dependencies": {
"@expo-google-fonts/inter": "^0.4.1",
"@expo/vector-icons": "^14.1.0",
"@react-native-community/blur": "^4.4.1",
"@react-navigation/bottom-tabs": "^7.3.10",
"@react-navigation/native": "^7.1.6",
"@react-navigation/native-stack": "^7.3.24",
"axios": "^1.11.0",
"dayjs": "^1.11.13",
"expo": "~53.0.20",
"expo-blur": "~14.1.5",
"expo-constants": "~17.1.7",
"expo-font": "~13.3.2",
"expo-haptics": "~14.1.4",
"expo-image": "~2.4.0",
"expo-linking": "~7.1.7",
"expo-local-authentication": "^16.0.5",
"expo-secure-store": "^14.2.3",
"expo-splash-screen": "~0.30.10",
"expo-status-bar": "~2.2.3",
"expo-symbols": "~0.4.5",
"expo-system-ui": "~5.0.10",
"expo-web-browser": "~14.2.0",
"react": "19.0.0",
"react-dom": "19.0.0",
"react-native": "0.79.5",
"react-native-gesture-handler": "~2.24.0",
"react-native-reanimated": "~3.17.4",
"react-native-root-toast": "^4.0.1",
"react-native-safe-area-context": "5.4.0",
"react-native-screens": "~4.11.1",
"react-native-swiper": "^1.6.0",
"react-native-webview": "13.13.5"
},
"devDependencies": {
"@babel/core": "^7.25.2",
"@types/react": "~19.0.10",
"eslint": "^9.25.0",
"eslint-config-expo": "~9.2.0",
"typescript": "~5.8.3"
},
"private": true
}