I have a small React Native app I'm developing and I'm using react-navigation/native. I have a minor bug on the page and I can't for the life of me figure it out. But first, here is my Navigation stack page:
import { NavigationContainer } from "@react-navigation/native";
import { createNativeStackNavigator } from "@react-navigation/native-stack";
import LoadPage from "./screens/LoadPage";
import WelcomePage from "./screens/WelcomePage";
import Signup from "./screens/Signup";
import Login from "./screens/Login";
import ResetPassword from "./screens/ResetPassword";
import Dashboard from "./screens/Dashboard";
import Onboarding1 from "./screens/Onboarding1";
import Onboarding2 from "./screens/Onboarding2";
import Onboarding3 from "./screens/Onboarding3";
import { GeneralAppProvider } from "./context/GeneralAppContext";
const Stack = createNativeStackNavigator();
export default function App() {
return (
<GeneralAppProvider>
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="Home"
component={LoadPage}
options={{ headerShown: false, gestureEnabled: false }}
/>
<Stack.Screen
name="Welcome"
component={WelcomePage}
options={{ headerShown: false, gestureEnabled: false }}
/>
<Stack.Screen
name="Signup"
component={Signup}
options={{ headerShown: false, gestureEnabled: false }}
/>
<Stack.Screen
name="Login"
component={Login}
options={{ headerShown: false, gestureEnabled: false }}
/>
<Stack.Screen
name="ResetPassword"
component={ResetPassword}
options={{ headerShown: false, gestureEnabled: false }}
/>
<Stack.Screen
name="Onboarding1"
component={Onboarding1}
options={{ headerShown: false, gestureEnabled: false }}
/>
<Stack.Screen
name="Onboarding2"
component={Onboarding2}
options={{ headerShown: false, gestureEnabled: false }}
/>
<Stack.Screen
name="Onboarding3"
component={Onboarding3}
options={{ headerShown: false, gestureEnabled: false }}
/>
<Stack.Screen
name="Dashboard"
component={Dashboard}
options={{ headerShown: false, gestureEnabled: false }}
/>
</Stack.Navigator>
</NavigationContainer>
</GeneralAppProvider>
);
}
LoadPage is my entry page into the app and it looks like this below. It is designed to navigate to either of two screens Welcome page for users not logged in and Dashboard page for users already logged in depending on if I have a user session(I'm managing this with the help of supabase):
import { useFonts, Poppins_600SemiBold } from "@expo-google-fonts/poppins";
import { useEffect } from "react";
import { Text, View } from "react-native";
import { useGeneralAppContext } from "../utils/useGeneralAppContext";
import AsyncStorage from "@react-native-async-storage/async-storage";
export default function LoadPage({ navigation }) {
const { session, generalDispatch } = useGeneralAppContext()
const expirationTime = session?.expires_at;
const currentTimestamp = Math.floor(Date.now() / 1000); // Current timestamp in seconds
const hasExpired = currentTimestamp >= expirationTime;
console.log(currentTimestamp, expirationTime)
useEffect(() => {
async function removeUserInfo() {
try {
await AsyncStorage.removeItem('user');
} catch (error) {
console.error(error)
}
}
function goToPage() {
if (session && !hasExpired) {
navigation.replace('Dashboard')
} else {
removeUserInfo()
generalDispatch({
type: 'setUser',
payload: {
userPayload: null,
},
});
navigation.replace('Welcome')
}
}
setTimeout(() => {
goToPage()
generalDispatch({
type: 'setLoadPageShown',
payload: {
loadPageShownPayload: true,
},
});
}, 3000)
}, [])
let [fontsLoaded, fontsError] = useFonts({
Poppins_600SemiBold
})
if (!fontsLoaded && !fontsError) {
return null;
}
return (
<View className="flex-1 items-center justify-center bg-white">
<Text style={{ fontFamily: 'Poppins_600SemiBold' }} className='text-[#1e1e1e] text-[32px]'>Card Vault</Text>
</View>
)
}
Now, when i try to log in or signup a user successfully as seen below, It redirects back to the LoadPage instead of the Dashboard or Onboarding page(based on if it's a login or signup session). The log in page looks like this
import { useFonts, Poppins_600SemiBold, Poppins_400Regular, Poppins_500Medium } from "@expo-google-fonts/poppins";
import { useState } from "react";
import { ActivityIndicator, Image, SafeAreaView, ScrollView, Text, TextInput, TouchableOpacity, View } from "react-native";
import Icon from 'react-native-vector-icons/AntDesign';
import { Keyboard, TouchableWithoutFeedback } from 'react-native'
import FaIcon from 'react-native-vector-icons/FontAwesome5'
import { supabase } from "../utils/supabase";
import { useGeneralAppContext } from "../utils/useGeneralAppContext";
import AsyncStorage from "@react-native-async-storage/async-storage";
export default function Login({ navigation }) {
let [fontsLoaded, fontsError] = useFonts({
Poppins_600SemiBold,
Poppins_400Regular,
Poppins_500Medium
})
const [loading, setLoading] = useState(false)
const [showPassword, setShowPassword] = useState(false)
const [error, setError] = useState({
type: '',
message: ''
})
const [userInfo, setUserInfo] = useState({
email: '',
password: '',
passwordConfirm: ''
})
const { email, password } = userInfo
const { generalDispatch } = useGeneralAppContext()
async function handleLogin() {
// Regular expression for validating an Email
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (email === '') {
setError({
type: 'Error',
message: 'Email cannot be empty'
})
} else if (password === '') {
setError({
type: 'Error',
message: 'Password cannot be empty'
})
} else if (!emailRegex.test(email)) {
setError({
type: 'Error',
message: 'Enter a valid email'
})
} else {
try {
setLoading(true)
let { data, error } = await supabase.auth.signInWithPassword({
email: email,
password: password
})
console.log(data, error)
if (error) {
setError({
type: 'Error',
message: error.message
})
} else {
generalDispatch({
type: 'setUserSession',
payload: {
sessionPayload: data.session
}
})
generalDispatch({
type: 'setUser',
payload: {
userPayload: data.user
}
})
await AsyncStorage.setItem('user', JSON.stringify(data.user));
navigation.replace('Dashboard')
}
} catch (error) {
console.error(error)
} finally {
setLoading(false)
}
}
setTimeout(() => {
setError({ type: '', message: '' })
}, 3000)
}
if (!fontsLoaded && !fontsError) {
return null;
}
return (
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
<SafeAreaView>
<ScrollView className='p-6'>
<TouchableOpacity onPress={() => navigation.goBack()}>
<Icon
name="arrowleft"
size={24}
/>
</TouchableOpacity>
<Text style={{ fontFamily: 'Poppins_600SemiBold' }} className='text-[#1E1E1E] mt-8 text-[24px]'>Reset Password</Text>
<View className='mt-8'>
<View className=''>
<Text style={{ fontFamily: 'Poppins_400Regular' }} className='text-sm text-[#1E1E1E]'>Email</Text>
<TextInput
placeholder="Enter your email address"
value={email}
style={{ fontFamily: 'Poppins_400Regular' }}
onChangeText={text => setUserInfo((prevInfo) => ({ ...prevInfo, email: text }))}
keyboardType="email-address"
className='bg-transparent rounded-lg border-[1px] mt-2 border-[#E0E0E0] py-3 px-4 focus:border-[1px] focus:border-[#000000]'
/>
</View>
<View className='mt-8 '>
<Text style={{ fontFamily: 'Poppins_400Regular' }} className='text-sm text-[#1E1E1E]'>Password</Text>
<View className='flex flex-row items-center relative'>
<TextInput
placeholder="Enter your password"
value={password}
onChangeText={text => setUserInfo((prevInfo) => ({ ...prevInfo, password: text }))}
secureTextEntry={showPassword ? false : true}
style={{ fontFamily: 'Poppins_400Regular' }}
className='flex-1 bg-blue-300w-full rounded-lg border-[1px] mt-2 border-[#E0E0E0] py-3 px-4 focus:border-[#000000]'
/>
<TouchableOpacity onPress={() => setShowPassword(!showPassword)} className='absolute right-4 bottom-3'>
<FaIcon
name={showPassword ? 'eye' : 'eye-slash'}
size={20}
/>
</TouchableOpacity>
</View>
</View>
<TouchableOpacity onPress={() => navigation.navigate('ResetPassword')}>
<Text style={{ fontFamily: 'Poppins_400Regular' }} className='text-[#4169E1] mt-2'>Forgot Password?</Text>
</TouchableOpacity>
<View className={`w-full p-3 rounded-md mt-8 border-[1px] border-[#b2afaf] bg-red-400 ${error.message === '' ? 'hidden' : ''}`}>
<Text style={{ fontFamily: 'Poppins_500Medium' }} className='text-white'>{error.message}</Text>
</View>
</View>
<View className='mt-20'>
<TouchableOpacity className='mt-12 w-full' onPress={handleLogin} disabled={loading}>
<View className='bg-[#4169E1] py-[18px] flex items-center rounded-xl'>
{loading ?
<ActivityIndicator size="small" color="#ffffff" /> :
<Text style={{ fontFamily: 'Poppins_400Regular' }} className='text-[#ffffff] text-sm'>Login</Text>
}
</View>
</TouchableOpacity>
{/* <View className='flex flex-row justify-between items-center py-8 gap-4'>
<View className='flex-1 border-b-[1px] border-[#a7a9ab]'></View>
<Text className='text-[#a7a9ab]'>OR</Text>
<View className='flex-1 border-b-[1px] border-[#a7a9ab]'></View>
</View>
<TouchableOpacity className='w-full'>
<View className='border-[1px] border-[#DADADA] py-[18px] flex flex-row justify-center items-center rounded-xl'>
<Image
source={require('../assets/flat-color-icons-google.png')}
/>
<Text style={{ fontFamily: 'Poppins_500Medium' }} className='text-[#1C1C1C] ml-2 text-sm'>Continue with Google</Text>
</View>
</TouchableOpacity> */}
</View>
<View className='flex flex-row items-center gap-1 justify-center mt-10'>
<Text style={{ fontFamily: 'Poppins_400Regular' }} className='text-[#1E1E1E]'>Don't have an account?</Text>
<TouchableOpacity onPress={() => navigation.navigate('Signup')}>
<Text style={{ fontFamily: 'Poppins_400Regular' }} className='text-[#4169E1]'>Sign Up</Text>
</TouchableOpacity>
</View>
</ScrollView>
</SafeAreaView>
</TouchableWithoutFeedback>
)
}
Same thing happens if the user is logged in and I try to logout from the dashboard page. It redirects to the LoadPage before redirecting again.
The user is supposed to be directed straight to the page specified on the navigation. I've tried using the replace, push, pop navigate methods on navigation, but everything still happens the same way.
I think it may be because this function:
needs to also be async, and then await
removeUserInfo()Something like this: