I am struggling to figure out a way to check if a is logged-in after the first launch of the app and then sending them to the MainStack which contains every screen after the AuthStack. I'm using FireBase for auth.
My Auth stack contains my signin, signup, and forgot password screen:
export default function AuthStack() {
return (
<Stack.Navigator
initialRouteName="Login"
screenOptions={{
headerShown: false
}}>
<Stack.Screen name="Login" component={LoginScreen} />
<Stack.Screen name="ForgotPassword" component={ForgotPasswordScreen} />
<Stack.Screen name="Registration" component={RegistrationScreen} />
</Stack.Navigator>
);
}
This is my Main Stack, which is a drawer navigator:
export default function MainStack() {
return (
<Drawer.Navigator
drawerContent={props => <CustomDrawer {...props} />}
initialRouteName="Home"
screenOptions={{
drawerActiveBackgroundColor: '#00BFFF',
drawerActiveTintColor: '#fff',
drawerInactiveTintColor: '#333',
headerTitleAlign: 'center',
headerTintColor: '#00BFFF',
drawerLabelStyle: {
marginLeft: -25,
fontSize: 15
},
}}>
<Drawer.Screen name="Home" component={HomeScreen} options={{
drawerIcon: ({ color }) => (
<Ionicons name="home-outline" size={22} color={color} />
)
}} />
<Drawer.Screen name='Calendar' component={CalendarScreen} options={{
drawerIcon: ({ color }) => (
<Ionicons name="calendar-outline" size={22} color={color} />
)
}} />
<Drawer.Screen name='Chat' component={ChatScreen} options={{
drawerIcon: ({ color }) => (
<Ionicons name="call-outline" size={22} color={color} />
)
}} />
<Drawer.Screen name='Goals' component={GoalsScreen} options={{
drawerIcon: ({ color }) => (
<Ionicons name="checkmark-circle-outline" size={22} color={color} />
)
}} />
<Drawer.Screen name='Journal' component={JournalScreen} options={{
drawerIcon: ({ color }) => (
<Ionicons name="journal-outline" size={22} color={color} />
)
}} />
<Drawer.Screen name='Resources' component={ResourcesScreen} options={{
drawerIcon: ({ color }) => (
<Ionicons name="search-outline" size={22} color={color} />
)
}} />
<Drawer.Screen name='User Profile' component={UserProfileScreen} options={{
drawerIcon: ({ color }) => (
<Ionicons name="person-outline" size={22} color={color} />
)
}} />
</Drawer.Navigator>
Lastly, here lies the issue. These are my routes, but I can't figure out how to send the user to the MainStack after they initially login. The last statement works perfectly fine, but the error is in the 'else if', I can't seem to incorporate authenticating the user and verifying if it's the first app launch at the same time.
export default function Routes() {
const { user, setUser } = useContext(AuthContext);
const [loading, setLoading] = useState(true);
const [initializing, setInitializing] = useState(true);
const [isFirstLaunch, setIsFirstLaunch] = useState(null);
function onAuthStateChanged(user) {
setUser(user);
if (initializing) {
setInitializing(false);
}
setLoading(false);
}
useEffect(() => {
const subscriber = auth.onAuthStateChanged(onAuthStateChanged);
return subscriber;
}, []);
useEffect(() => {
AsyncStorage.getItem('alreadyLaunched').then(value => {
if (value === null) {
AsyncStorage.setItem('alreadyLaunched', 'true');
setIsFirstLaunch(true);
}
else {
setIsFirstLaunch(false);
}
})
}, []);
if (isFirstLaunch === null) {
return null;
}
else if (isFirstLaunch === true) {
return (
<NavigationContainer>
<Stack.Navigator
screenOptions={{
headerShown: false
}}>
<Stack.Screen name='OnBoarding' component={OnBoardingScreen} />
<Stack.Screen name="Login" component={LoginScreen} />
<Stack.Screen name="ForgotPassword" component={ForgotPasswordScreen} />
<Stack.Screen name="Registration" component={RegistrationScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
else {
return (
<NavigationContainer>
{user ? <MainStack /> : <AuthStack />}
</NavigationContainer>
)
}
Below is AuthProvider with firebase
import React, { createContext, useState } from 'react';
import { auth } from '../firebase/firebase';
export const AuthContext = createContext({});
export const AuthProvider = ({ children }) => {
const [user, setUser] = useState(null);
return (
<AuthContext.Provider
value={{
user,
setUser,
login: async (email, password) => {
try {
await auth.signInWithEmailAndPassword(email, password);
} catch (error) {
console.log(error);
}
},
register: async (email, password) => {
try {
await auth.createUserWithEmailAndPassword(email, password);
} catch (error) {
console.log(error);
}
},
logout: async () => {
try {
await auth.signOut();
} catch (error) {
console.log(error);
}
}
}}
>
{children}
</AuthContext.Provider>
)
}
CodePudding user response:
I'm not good at react-native but what I think in this case is that you need to add MainStack as Stack.Screen in AuthStack, so when you change route you would be moving to MainStack
sth like this :
export default function AuthStack() {
return (
<Stack.Navigator
initialRouteName="Login"
screenOptions={{
headerShown: false
}}>
<Stack.Screen name="Login" component={LoginScreen} />
<Stack.Screen name="ForgotPassword" component={ForgotPasswordScreen} />
<Stack.Screen name="Registration" component={RegistrationScreen} />
<Stack.Screen name="Main" component={MainStack} />
</Stack.Navigator>
);
}
and in login button :
<Button
title="Login"
onPress={() => navigation.navigate('Main')}
/>
CodePudding user response:
The logic you have to check whether it's the first launch can be modified. You can display a splash screen on your app until you fetch the required info to check whether there is a logged in user and then hide the splash screen. And based on whether there is a user or not, you can load different navigators from your App.js
file.
Here's what that might look like if you're using Expo with React Native:
import * as SplashScreen from "expo-splash-screen";
import { useState, useEffect } from "react";
export default function App() {
const [appReady, setAppReady] = useState(false);
const [user, setUser] = useState();
useEffect(() => {
async function prepare() {
try {
// Keep the splash screen visible while we fetch resources
await SplashScreen.preventAutoHideAsync();
// Make any API calls you need to do here
const user = await authStorage.getUser();
if (!user) return;
setUser(user);
} catch (e) {
logger.log(e);
} finally {
// Tell the application to render
setAppReady(true);
await SplashScreen.hideAsync();
}
}
prepare();
}, []);
if (!appReady) {
return null;
}
return (
<NavigationContainer>
{user ? <MainStack /> : <AuthStack />}
</NavigationContainer>
);
}
When you need to log the user out, simply set the user to null
. You're probably going to want to set this state from another component inside one of your screens. And to be able to manipulate the user state in the App.js
file from a child component without prop drilling, we can use the useContext
hook. So your code can look something like this:
// context.js
import React from "react";
const AuthContext = React.createContext();
export default AuthContext;
// App.js
import AuthContext from "./context";
...
const [user, setUser] = useState();
...
<AuthContext.Provider value={{ user, setUser }}>
<NavigationContainer>
{user ? <FeedNavigator /> : <AuthNavigator />}
</NavigationContainer>
</AuthContext.Provider>
And in a child component, you can access this state and set the user to null
when the click on the logout button:
// child.js
import { useContext } from "react";
import AuthContext from "./context";
export default Child = () => {
const { setUser } = useContext(AuthContext);
const handleLogout = () => {
setUser(null);
// Log user out using firebase fn
};
return(
<Button onPress={handleLogout}>
Logout
</Button>
)
};