reactjstypescriptreact-nativeexpohybrid-mobile-app

got error while trying AuthStack for user Authentication


I'm new to React Native, and I'm facing an issue when trying to fetch the user token to check if the user is authenticated. If the token is found, I consider the user authenticated and attempt to display the root stack. However, the problem I'm encountering is that the screens from the AuthStack are still being mounted on top of the RootStack. Can Someone please help me

below is my code:

import { router, Slot, Stack, Tabs } from "expo-router";
import { StatusBar } from "expo-status-bar";
import { useContext, useEffect, useState } from "react";
import { AuthContext, AuthContextProvider } from "../context/AuthContext";
import { ActivityIndicator, View } from "react-native";
import AsyncStorage from "@react-native-async-storage/async-storage";
import { getUserInforWithToken } from "../controller/AuthController";
function RootStack() {
  return (
    <Stack>
      <Stack.Screen name="(root)/index" options={{ title: "Welcome" }} />
    </Stack>
  );
}
function AuthStack() {
  return (
    <Stack
      screenOptions={{
        headerStyle: { backgroundColor: "#6779ef" },
        headerTintColor: "white",
        contentStyle: { backgroundColor: "#6779ef" },
        headerShown: false,
      }}>
      <Stack.Screen name="(auth)/index" options={{ title: "Login Page" }} />
      <Stack.Screen name="(auth)/signup" options={{ title: "Sign Up Page" }} />
    </Stack>
  );
}
function Root() {
  const { setIsLogin, setToken, setIsTryingLogin, isTryingLogin } =
    useContext(AuthContext);
  const [isLoading, setIsLoading] = useState(true);
  const { isLogin } = useContext(AuthContext);

  useEffect(() => {
    const checkAuth = async () => {
      const token = await AsyncStorage.getItem("token");
      if (token) {
        console.log("token found, user logged in");
        setIsLogin(true);
        setToken(token);
      } else {
        setIsLogin(false);
      }
      // Set trying login to false after we check for token
      setIsTryingLogin(false);
    };

    checkAuth();
  }, []); // This runs only once on component mount

  useEffect(() => {
    if (!isTryingLogin) {
      setIsLoading(false);
    }
      }, [isTryingLogin]);
      if (isLoading) {
        console.log("Loading...");
        return (
          <View className="flex-1 justify-center items-center">
           <ActivityIndicator className="p-2 m-2" animating size="large" />
          </View>
         );
       }
      return isLogin ? <RootStack /> : <AuthStack />;
    }

    export default function Layout() {
      return (
        <AuthContextProvider>
          <StatusBar style="light" />
          <Root />
        </AuthContextProvider>
      );
    }

I tried Chatgpt and tried reading docs and even tried to follow someone youtube channel noting worked


Solution

  • The issue you're encountering is likely due to both stacks (RootStack and AuthStack) being mounted simultaneously in your current setup.

    instead of rendering both stack conditionally In the root stack, conditionally render the correct stack in the stack navigator itself based on the isLogin state.

    eg.

    import { router, Slot, Stack, Tabs } from "expo-router";
    import { StatusBar } from "expo-status-bar";
    import { useContext, useEffect, useState } from "react";
    import { AuthContext, AuthContextProvider } from "../context/AuthContext";
    import { ActivityIndicator, View } from "react-native";
    import AsyncStorage from "@react-native-async-storage/async-storage";
    
    function Root() {
      const { setIsLogin, setToken, setIsTryingLogin, isTryingLogin, isLogin } =
        useContext(AuthContext);
      const [isLoading, setIsLoading] = useState(true);
    
      useEffect(() => {
        const checkAuth = async () => {
          const token = await AsyncStorage.getItem("token");
          if (token) {
            console.log("token found, user logged in");
            setIsLogin(true);
            setToken(token);
          } else {
            setIsLogin(false);
          }
          // Set trying login to false after we check for token
          setIsTryingLogin(false);
        };
    
        checkAuth();
      }, []); // This runs only once on component mount
    
      useEffect(() => {
        if (!isTryingLogin) {
          setIsLoading(false);
        }
      }, [isTryingLogin]);
    
      if (isLoading) {
        console.log("Loading...");
        return (
          <View className="flex-1 justify-center items-center">
            <ActivityIndicator className="p-2 m-2" animating size="large" />
          </View>
        );
      }
    
      return (
        <Stack
          screenOptions={{
            headerShown: false, // Hides header for all screens by default
          }}
        >
          {isLogin ? (
            <>
              {/* Root Stack for authenticated users */}
              <Stack.Screen
                name="(root)/index"
                component={RootStack}
                options={{ title: "Welcome" }}
              />
            </>
          ) : (
            <>
              {/* Auth Stack for unauthenticated users */}
              <Stack.Screen
                name="(auth)/index"
                component={AuthStack}
                options={{ title: "Login Page" }}
              />
              <Stack.Screen
                name="(auth)/signup"
                component={AuthStack}
                options={{ title: "Sign Up Page" }}
              />
            </>
          )}
        </Stack>
      );
    }
    
    function RootStack() {
      return (
        <Stack>
          <Stack.Screen name="(root)/index" options={{ title: "Welcome" }} />
        </Stack>
      );
    }
    
    function AuthStack() {
      return (
        <Stack
          screenOptions={{
            headerStyle: { backgroundColor: "#6779ef" },
            headerTintColor: "white",
            contentStyle: { backgroundColor: "#6779ef" },
            headerShown: false,
          }}
        >
          <Stack.Screen name="(auth)/index" options={{ title: "Login Page" }} />
          <Stack.Screen name="(auth)/signup" options={{ title: "Sign Up Page" }} />
        </Stack>
      );
    }
    
    export default function Layout() {
      return (
        <AuthContextProvider>
          <StatusBar style="light" />
          <Root />
        </AuthContextProvider>
      );
    }