我正在开发一个小型 React Native 应用程序,并且正在使用 React-navigation/native。我的页面上有一个小错误,我一生都无法弄清楚。但首先,这是我的导航堆栈页面:
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 是我进入应用程序的入口页面,如下所示。它旨在导航到两个屏幕中的任何一个:未登录的用户的欢迎页面和已登录的用户的仪表板页面,具体取决于我是否有用户会话(我在 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>
)
}
现在,当我尝试成功登录或注册用户时,如下所示,它会重定向回 LoadPage 而不是仪表板或入职页面(基于是否是登录或注册会话)。 登录页面是这样的
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>
)
}
如果用户已登录并且我尝试从仪表板页面注销,也会发生同样的情况。它会在再次重定向之前重定向到 LoadPage。
用户应该被直接定向到导航上指定的页面。我尝试过在导航上使用替换、推送、弹出导航方法,但一切仍然以相同的方式发生。
我想可能是因为这个功能:
function goToPage() {
if (session && !hasExpired) {
navigation.replace('Dashboard')
} else {
removeUserInfo()
generalDispatch({ type: 'setUser', payload: { userPayload: null } });
navigation.replace('Welcome')
}
}
也需要异步,然后等待
removeUserInfo()
类似这样的:
async function goToPage() {
if (session && !hasExpired) {
navigation.replace('Dashboard')
} else {
await removeUserInfo()
generalDispatch({ type: 'setUser', payload: { userPayload: null } });
navigation.replace('Welcome')
}
}