使用 AsyncStorage 登录后,React Native React 导航屏幕不会重定向
React Native React Navigation screen does not redirect after login with AsyncStorage
所以我用 React Native、React Navigation 和 AsyncStorage 构建了一个登录系统。如果用户单击一个按钮,他就会登录,并且 AsyncStorage 键“@loginuser”的值会被刷新。现在我期望
屏幕自动刷新,但我必须关闭应用程序并重新启动它 - 这不是最佳选择。
(我也看到了React-Native/React navigation redirection after login with reduxpost,但是很老了)
App.js
import React from 'react';
import { Text, View, StyleSheet, Button, TouchableOpacity, TextInput } from 'react-native';
import DefaultStackNavigation from './components/Navigation/Navigation';
const App = () => {
return(
<View>
<DefaultStackNavigation />
</View>
)
}
export default App;
Navigation.js
import React, {useEffect, useState} from 'react';
//React Native
import { Text, View, StyleSheet} from 'react-native';
//Screens
import HomeScreen from '../HomeScreen/HomeScreen'
import AddScreen from "../AddScreen/AddScreen";
import NotificationScreen from "../NotificationScreen/NotificationScreen";
import MenuScreen from "../MenuScreen/MenuScreen";
import SearchScreen from "../SearchScreen/SearchScreen";
import PostJobScreen from "../PostJobScreen/PostJobScreen";
import JobOfferScreen from "../JobOfferScreen/JobOfferScreen";
import ProfileScreen from "../ProfileScreen/ProfileScreen";
import NoneLoggedinScreen from "../NoneLoggedinScreen/NoneLoggedinScreen"
import SignupModal from "../NoneLoggedinScreen/SignupModal"
//React Navigation
import { createStackNavigator } from "@react-navigation/stack";
import { NavigationContainer } from '@react-navigation/native';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
//Third Party
import AsyncStorage from '@react-native-async-storage/async-storage';
const Tab = createBottomTabNavigator();
//Tab bar
const HomeTabs = () => {
return (
<Tab.Navigator tabBarOptions={{style:{height: 50}}, {showIcon: true}, {showLabel: false}} >
<Tab.Screen name="Home" component={HomeScreen}/>
<Tab.Screen name="SearchScreen" component={SearchJobStack}/>
<Tab.Screen name="AddScreen" component={AddScreen}/>
<Tab.Screen name="NotificationScreen" component={NotificationScreen} />}}/>
<Tab.Screen name="MenuScreen" component={MenuScreen}/>}}/>
</Tab.Navigator>
);
}
const Stack = createStackNavigator();
const STORAGE_KEY = '@loginStatus'
const DefaultStackNavigation = () => {
const [loginStatus, setLoginStatus] = useState()
const readData = async () => {
try {
const isLoggedIn = JSON.parse(await AsyncStorage.getItem(STORAGE_KEY))
console.log(isLoggedIn)
if (isLoggedIn !== null) {
setLoginStatus(isLoggedIn)
}
} catch (e) {
alert('Failed to fetch the data from storage')
}
}
readData()
return loginStatus ? (
<NavigationContainer>
<Stack.Navigator screenOptions={{headerShown: false}} independent={false}>
<Stack.Screen name="HomeTabs" component={HomeTabs} />
<Stack.Screen name="PostJobScreen" component={PostJobScreen} />
<Stack.Screen name="ProfileScreen" component={ProfileScreen}/>
</Stack.Navigator>
</NavigationContainer>
) : (
<NavigationContainer>
<Stack.Navigator screenOptions={{headerShown: false}} independent={false}>
<Stack.Screen name="NoneLoggedinScreen" component={NoneLoggedinScreen} />
<Stack.Screen name="SignupModal" component={SignupModal} />
</Stack.Navigator>
</NavigationContainer>
);
};
export default DefaultStackNavigation;
NoneLoggedinScreen.js
import React, { useState, useEffect } from 'react';
import { Text, View, Button} from 'react-native';
import AsyncStorage from '@react-native-async-storage/async-storage';
const STORAGE_KEY = '@loginStatus'
const SignupModal = () => {
const [loginStatus, setLoginStatus] = useState(false)
const saveData = async (parmLoginStatus) => {
try {
await AsyncStorage.setItem(STORAGE_KEY, JSON.stringify(parmLoginStatus))
alert('Data successfully saved -> Logged In')
console.log(loginStatus)
} catch (e) {
alert('Failed to save the data to the storage')
}
}
const onSubmitLogin = () => {
setLoginStatus(true)
saveData(true)
}
return(
<View>
<Text>Login Page. Press the button to log in and stay logged in</Text>
<Button title="Log in" onPress={() => onSubmitLogin()}/>
</View>
);
};
const styles = StyleSheet.create({
App: {
flex: 1,
backgroundColor: "white"
},
});
export default SignupModal;
现在我期待页面重新加载并且我可以使用该应用程序。不幸的是它没有。数据已保存,登录状态设置为true
,但我必须重新启动应用程序才能使用它。背信弃义的是,如果我将 NoneLoggedinScreen.js
文件中的 AsyncStorage 登录逻辑写入 App.js 文件,该应用程序工作正常 -> 但是,这对我来说不是一个替代方案,因为一般结构我认为该应用程序的构建相对明智。
此外,当用户尝试在登录后使用 navigation.navigate("HomeTabs") 手动(通过按钮)重定向时不起作用,并且我收到一个错误,指出主页不存在,这也是不可理解的,因为现在导航实际上已经设置为已登录。有人遇到过这个问题吗?
顺便说一句,这是我的依赖项
"dependencies": {<br>
"@react-native-async-storage/async-storage": "^1.15.1",<br>
"@react-native-community/masked-view": "^0.1.10",<br>
"@react-navigation/bottom-tabs": "^5.11.7",<br>
"@react-navigation/native": "^5.9.2",<br>
"@react-navigation/stack": "^5.14.2",<br>
"react": "16.13.1",<br>
"react-native": "0.63.4",<br>
"react-native-gesture-handler": "^1.10.1",<br>
"react-native-reanimated": "^1.13.2",<br>
"react-native-safe-area-context": "^3.1.9",<br>
"react-native-screens": "^2.17.1",<br>
}
在您的身份验证策略中,有一个隐藏的假设,即当您在 NoneLoggedInScreen
中使用 AsyncStorage 保存数据时,将立即使用 DefaultStackNavigation
中的 readData
函数读取数据。这不会发生的原因是 readData
仅在 DefaultStackNavigation
renders/re-renders 时被调用。当其子项之一在本地存储中设置一些数据时,不会发生这种情况。
有很多方法可以解决这个问题。将 redux 与 redux-persist 或其他状态管理和持久性设置一起使用,或使用基于上下文的特定解决方案(例如,参见 Authentication in React Applications 上的这篇 Kent C. Dodds 文章)。
您提到的第二个小问题:
navigation.navigate("HomeTabs") after logging in doesn't work, and I get an error that Home doesn't exist
确实有道理,其背后的原因是你有条件地渲染了两个不同的NavigationContainers
。他们不知道彼此的路线,所以你不能在他们之间导航。
要解决此问题,您应该渲染一个 NavigationContainer
并有条件地将 Stack.Navigator
之一渲染为它的子项。
为了顺利地管理身份验证流程,我们应该为导航制作两个独立的导航器。我发现这个工作流程是目前最好的。它工作完美并且很干净。
所以我通常在我的项目中创建两个导航器 AuthNavigator.js
和 AppNavigator.js
,然后使用条件渲染。
因此,您可以做的是在 App.js
所在的位置创建一个名为 navigation
的文件夹。然后在 navigation
文件夹中创建两个名为 AppNavigator.js
和 AuthNavigator.js
.
的文件
你的 AuthNavigator.js
应该是这样的
import React from "react";
import NoneLoggedinScreen from "../NoneLoggedinScreen/NoneLoggedinScreen";
import SignupModal from "../NoneLoggedinScreen/SignupModal";
import { createStackNavigator } from "@react-navigation/stack";
const Stack = createStackNavigator();
const AuthNavigator = () => {
return (
<Stack.Navigator screenOptions={{ headerShown: false }} independent={false}>
<Stack.Screen name="NoneLoggedinScreen" component={NoneLoggedinScreen} />
<Stack.Screen name="SignupModal" component={SignupModal} />
</Stack.Navigator>
);
};
export default AuthNavigator;
你的 AppNavigator.js
应该是这样的
import React from "react";
import HomeScreen from "../HomeScreen/HomeScreen";
import AddScreen from "../AddScreen/AddScreen";
import NotificationScreen from "../NotificationScreen/NotificationScreen";
import MenuScreen from "../MenuScreen/MenuScreen";
import PostJobScreen from "../PostJobScreen/PostJobScreen";
import ProfileScreen from "../ProfileScreen/ProfileScreen";
import { createStackNavigator } from "@react-navigation/stack";
import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";
const Tab = createBottomTabNavigator();
//Tab bar
const HomeTabs = () => {
return (
<Tab.Navigator
tabBarOptions={
({ style: { height: 50 } }, { showIcon: true }, { showLabel: false })
}
>
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen name="SearchScreen" component={SearchJobStack} />
<Tab.Screen name="AddScreen" component={AddScreen} />
<Tab.Screen name="NotificationScreen" component={NotificationScreen} />
<Tab.Screen name="MenuScreen" component={MenuScreen} />
</Tab.Navigator>
);
};
const Stack = createStackNavigator();
const AppNavigator = () => {
return (
<Stack.Navigator screenOptions={{ headerShown: false }} independent={false}>
<Stack.Screen name="HomeTabs" component={HomeTabs} />
<Stack.Screen name="PostJobScreen" component={PostJobScreen} />
<Stack.Screen name="ProfileScreen" component={ProfileScreen} />
</Stack.Navigator>
);
};
export default AppNavigator;
你的 App.js
应该是这样的
import React, { useState, useEffect } from 'react';
import { NavigationContainer } from '@react-navigation/native';
import AuthStorage from './auth/storage';
import AppNavigator from './navigation/AppNavigator';
import AuthNavigator from './navigation/AuthNavigator';
const App = () => {
const [loginStatus, setLoginStatus] = useState(false);
// I am using useEffect hook but I would prefer using `AppLoading` to restore token if it exists
useEffect(() => {
readData();
}, []);
const readData = async () => {
try {
const isLoggedIn = JSON.parse(await AuthStorage.getItem());
console.log(isLoggedIn);
if (isLoggedIn !== null) {
setLoginStatus(isLoggedIn);
}
} catch (e) {
alert('Failed to fetch the data from storage');
}
};
return (
<NavigationContainer>
{loginStatus ? <AppNavigator /> : <AuthNavigator />}
</NavigationContainer>
);
};
export default App;
在您的 App.js
所在的位置创建一个名为 auth
的文件夹。在其中创建一个名为 storage.js
的文件。现在在 storage.js
中粘贴此代码
import AsyncStorage from '@react-native-async-storage/async-storage';
const AuthToken = '@loginStatus';
const storeItem = async (value) => {
try {
await AsyncStorage.setItem(AuthToken, JSON.stringify(value));
return true;
} catch (e) {
return false;
}
};
const getItem = async () => {
try {
const jsonValue = await AsyncStorage.getItem(AuthToken);
return jsonValue != null ? JSON.parse(jsonValue) : null;
} catch (e) {
return null;
}
};
export default { storeItem, getItem };
所以我用 React Native、React Navigation 和 AsyncStorage 构建了一个登录系统。如果用户单击一个按钮,他就会登录,并且 AsyncStorage 键“@loginuser”的值会被刷新。现在我期望 屏幕自动刷新,但我必须关闭应用程序并重新启动它 - 这不是最佳选择。
(我也看到了React-Native/React navigation redirection after login with reduxpost,但是很老了)
App.js
import React from 'react';
import { Text, View, StyleSheet, Button, TouchableOpacity, TextInput } from 'react-native';
import DefaultStackNavigation from './components/Navigation/Navigation';
const App = () => {
return(
<View>
<DefaultStackNavigation />
</View>
)
}
export default App;
Navigation.js
import React, {useEffect, useState} from 'react';
//React Native
import { Text, View, StyleSheet} from 'react-native';
//Screens
import HomeScreen from '../HomeScreen/HomeScreen'
import AddScreen from "../AddScreen/AddScreen";
import NotificationScreen from "../NotificationScreen/NotificationScreen";
import MenuScreen from "../MenuScreen/MenuScreen";
import SearchScreen from "../SearchScreen/SearchScreen";
import PostJobScreen from "../PostJobScreen/PostJobScreen";
import JobOfferScreen from "../JobOfferScreen/JobOfferScreen";
import ProfileScreen from "../ProfileScreen/ProfileScreen";
import NoneLoggedinScreen from "../NoneLoggedinScreen/NoneLoggedinScreen"
import SignupModal from "../NoneLoggedinScreen/SignupModal"
//React Navigation
import { createStackNavigator } from "@react-navigation/stack";
import { NavigationContainer } from '@react-navigation/native';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
//Third Party
import AsyncStorage from '@react-native-async-storage/async-storage';
const Tab = createBottomTabNavigator();
//Tab bar
const HomeTabs = () => {
return (
<Tab.Navigator tabBarOptions={{style:{height: 50}}, {showIcon: true}, {showLabel: false}} >
<Tab.Screen name="Home" component={HomeScreen}/>
<Tab.Screen name="SearchScreen" component={SearchJobStack}/>
<Tab.Screen name="AddScreen" component={AddScreen}/>
<Tab.Screen name="NotificationScreen" component={NotificationScreen} />}}/>
<Tab.Screen name="MenuScreen" component={MenuScreen}/>}}/>
</Tab.Navigator>
);
}
const Stack = createStackNavigator();
const STORAGE_KEY = '@loginStatus'
const DefaultStackNavigation = () => {
const [loginStatus, setLoginStatus] = useState()
const readData = async () => {
try {
const isLoggedIn = JSON.parse(await AsyncStorage.getItem(STORAGE_KEY))
console.log(isLoggedIn)
if (isLoggedIn !== null) {
setLoginStatus(isLoggedIn)
}
} catch (e) {
alert('Failed to fetch the data from storage')
}
}
readData()
return loginStatus ? (
<NavigationContainer>
<Stack.Navigator screenOptions={{headerShown: false}} independent={false}>
<Stack.Screen name="HomeTabs" component={HomeTabs} />
<Stack.Screen name="PostJobScreen" component={PostJobScreen} />
<Stack.Screen name="ProfileScreen" component={ProfileScreen}/>
</Stack.Navigator>
</NavigationContainer>
) : (
<NavigationContainer>
<Stack.Navigator screenOptions={{headerShown: false}} independent={false}>
<Stack.Screen name="NoneLoggedinScreen" component={NoneLoggedinScreen} />
<Stack.Screen name="SignupModal" component={SignupModal} />
</Stack.Navigator>
</NavigationContainer>
);
};
export default DefaultStackNavigation;
NoneLoggedinScreen.js
import React, { useState, useEffect } from 'react';
import { Text, View, Button} from 'react-native';
import AsyncStorage from '@react-native-async-storage/async-storage';
const STORAGE_KEY = '@loginStatus'
const SignupModal = () => {
const [loginStatus, setLoginStatus] = useState(false)
const saveData = async (parmLoginStatus) => {
try {
await AsyncStorage.setItem(STORAGE_KEY, JSON.stringify(parmLoginStatus))
alert('Data successfully saved -> Logged In')
console.log(loginStatus)
} catch (e) {
alert('Failed to save the data to the storage')
}
}
const onSubmitLogin = () => {
setLoginStatus(true)
saveData(true)
}
return(
<View>
<Text>Login Page. Press the button to log in and stay logged in</Text>
<Button title="Log in" onPress={() => onSubmitLogin()}/>
</View>
);
};
const styles = StyleSheet.create({
App: {
flex: 1,
backgroundColor: "white"
},
});
export default SignupModal;
现在我期待页面重新加载并且我可以使用该应用程序。不幸的是它没有。数据已保存,登录状态设置为true
,但我必须重新启动应用程序才能使用它。背信弃义的是,如果我将 NoneLoggedinScreen.js
文件中的 AsyncStorage 登录逻辑写入 App.js 文件,该应用程序工作正常 -> 但是,这对我来说不是一个替代方案,因为一般结构我认为该应用程序的构建相对明智。
此外,当用户尝试在登录后使用 navigation.navigate("HomeTabs") 手动(通过按钮)重定向时不起作用,并且我收到一个错误,指出主页不存在,这也是不可理解的,因为现在导航实际上已经设置为已登录。有人遇到过这个问题吗?
顺便说一句,这是我的依赖项
"dependencies": {<br>
"@react-native-async-storage/async-storage": "^1.15.1",<br>
"@react-native-community/masked-view": "^0.1.10",<br>
"@react-navigation/bottom-tabs": "^5.11.7",<br>
"@react-navigation/native": "^5.9.2",<br>
"@react-navigation/stack": "^5.14.2",<br>
"react": "16.13.1",<br>
"react-native": "0.63.4",<br>
"react-native-gesture-handler": "^1.10.1",<br>
"react-native-reanimated": "^1.13.2",<br>
"react-native-safe-area-context": "^3.1.9",<br>
"react-native-screens": "^2.17.1",<br>
}
在您的身份验证策略中,有一个隐藏的假设,即当您在 NoneLoggedInScreen
中使用 AsyncStorage 保存数据时,将立即使用 DefaultStackNavigation
中的 readData
函数读取数据。这不会发生的原因是 readData
仅在 DefaultStackNavigation
renders/re-renders 时被调用。当其子项之一在本地存储中设置一些数据时,不会发生这种情况。
有很多方法可以解决这个问题。将 redux 与 redux-persist 或其他状态管理和持久性设置一起使用,或使用基于上下文的特定解决方案(例如,参见 Authentication in React Applications 上的这篇 Kent C. Dodds 文章)。
您提到的第二个小问题:
navigation.navigate("HomeTabs") after logging in doesn't work, and I get an error that Home doesn't exist
确实有道理,其背后的原因是你有条件地渲染了两个不同的NavigationContainers
。他们不知道彼此的路线,所以你不能在他们之间导航。
要解决此问题,您应该渲染一个 NavigationContainer
并有条件地将 Stack.Navigator
之一渲染为它的子项。
为了顺利地管理身份验证流程,我们应该为导航制作两个独立的导航器。我发现这个工作流程是目前最好的。它工作完美并且很干净。
所以我通常在我的项目中创建两个导航器 AuthNavigator.js
和 AppNavigator.js
,然后使用条件渲染。
因此,您可以做的是在 App.js
所在的位置创建一个名为 navigation
的文件夹。然后在 navigation
文件夹中创建两个名为 AppNavigator.js
和 AuthNavigator.js
.
你的 AuthNavigator.js
应该是这样的
import React from "react";
import NoneLoggedinScreen from "../NoneLoggedinScreen/NoneLoggedinScreen";
import SignupModal from "../NoneLoggedinScreen/SignupModal";
import { createStackNavigator } from "@react-navigation/stack";
const Stack = createStackNavigator();
const AuthNavigator = () => {
return (
<Stack.Navigator screenOptions={{ headerShown: false }} independent={false}>
<Stack.Screen name="NoneLoggedinScreen" component={NoneLoggedinScreen} />
<Stack.Screen name="SignupModal" component={SignupModal} />
</Stack.Navigator>
);
};
export default AuthNavigator;
你的 AppNavigator.js
应该是这样的
import React from "react";
import HomeScreen from "../HomeScreen/HomeScreen";
import AddScreen from "../AddScreen/AddScreen";
import NotificationScreen from "../NotificationScreen/NotificationScreen";
import MenuScreen from "../MenuScreen/MenuScreen";
import PostJobScreen from "../PostJobScreen/PostJobScreen";
import ProfileScreen from "../ProfileScreen/ProfileScreen";
import { createStackNavigator } from "@react-navigation/stack";
import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";
const Tab = createBottomTabNavigator();
//Tab bar
const HomeTabs = () => {
return (
<Tab.Navigator
tabBarOptions={
({ style: { height: 50 } }, { showIcon: true }, { showLabel: false })
}
>
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen name="SearchScreen" component={SearchJobStack} />
<Tab.Screen name="AddScreen" component={AddScreen} />
<Tab.Screen name="NotificationScreen" component={NotificationScreen} />
<Tab.Screen name="MenuScreen" component={MenuScreen} />
</Tab.Navigator>
);
};
const Stack = createStackNavigator();
const AppNavigator = () => {
return (
<Stack.Navigator screenOptions={{ headerShown: false }} independent={false}>
<Stack.Screen name="HomeTabs" component={HomeTabs} />
<Stack.Screen name="PostJobScreen" component={PostJobScreen} />
<Stack.Screen name="ProfileScreen" component={ProfileScreen} />
</Stack.Navigator>
);
};
export default AppNavigator;
你的 App.js
应该是这样的
import React, { useState, useEffect } from 'react';
import { NavigationContainer } from '@react-navigation/native';
import AuthStorage from './auth/storage';
import AppNavigator from './navigation/AppNavigator';
import AuthNavigator from './navigation/AuthNavigator';
const App = () => {
const [loginStatus, setLoginStatus] = useState(false);
// I am using useEffect hook but I would prefer using `AppLoading` to restore token if it exists
useEffect(() => {
readData();
}, []);
const readData = async () => {
try {
const isLoggedIn = JSON.parse(await AuthStorage.getItem());
console.log(isLoggedIn);
if (isLoggedIn !== null) {
setLoginStatus(isLoggedIn);
}
} catch (e) {
alert('Failed to fetch the data from storage');
}
};
return (
<NavigationContainer>
{loginStatus ? <AppNavigator /> : <AuthNavigator />}
</NavigationContainer>
);
};
export default App;
在您的 App.js
所在的位置创建一个名为 auth
的文件夹。在其中创建一个名为 storage.js
的文件。现在在 storage.js
中粘贴此代码
import AsyncStorage from '@react-native-async-storage/async-storage';
const AuthToken = '@loginStatus';
const storeItem = async (value) => {
try {
await AsyncStorage.setItem(AuthToken, JSON.stringify(value));
return true;
} catch (e) {
return false;
}
};
const getItem = async () => {
try {
const jsonValue = await AsyncStorage.getItem(AuthToken);
return jsonValue != null ? JSON.parse(jsonValue) : null;
} catch (e) {
return null;
}
};
export default { storeItem, getItem };