React Native认证与StackNavigator页面不刷新问题求助
问题
在安卓模拟器上运行React Native应用时,登录后无法自动跳转到首页,必须重启应用才会触发自动登录;注销操作同理,点击注销按钮后需重启应用才会生效,否则页面停留在原界面。JWT变更后应用不会自动刷新页面。
相关代码片段
App.jsx
import React, { useEffect } from 'react' import { AsyncStorage } from 'react-native' import moment from 'moment' import { NavigationContainer } from '@react-navigation/native' import { createNativeStackNavigator } from '@react-navigation/native-stack' import Login from './modules/Login' import Home from './modules/Home' import Nav from './modules/Nav' import Events from './modules/Events/Events' import ConsentForm from './modules/ConsentForm' export default function App() { const [expired, setExpired] = React.useState(true) const [consented, setConsented] = React.useState(false) const Stack = createNativeStackNavigator() const getData = async () => { return await AsyncStorage.getItem('@myapp:jwt'); } useEffect(() => { getData().then((value) => { value = JSON.parse(value) if(value == null){ setExpired(true) }else{ const now = moment() setExpired(now.isAfter(moment(value?.expiry))) setConsented(value?.consented) } }) }) return ( <NavigationContainer> <Stack.Navigator> {!expired ? ( consented ? ( <> <Stack.Screen name="Home" component={Home} /> <Stack.Screen name="Nav" component={Nav} /> <Stack.Screen name="Events" component={Events} /> </> ): ( <> <Stack.Screen name="ConsentForm" component={ConsentForm} /> </> ) ) : ( <> <Stack.Screen name="Login" component={Login} /> </> )} </Stack.Navigator> </NavigationContainer> )}
登录页面代码
import React from 'react' import { *, AsyncStorage} from 'react-native' // imported a bunch of stuff here import jwt_decode from 'jwt-decode' import { MY_API_URL } from './constants' const Login = ({ navigation }) => { const [username, onChangeUsername] = React.useState(null) const [password, onChangePassword] = React.useState(null) const postLoginInformation = () => { fetch(`${MY_API_URL}/login/`, { method: 'POST', headers: { Accept: 'application/json', 'Content-Type': 'application/json', }, body: JSON.stringify({ username: username, password: password, }), }) .then(async (res) => { const response = await res.json() const jwt = JSON.parse(response).jwt const decoded_jwt = jwt_decode(jwt) await AsyncStorage.setItem( '@myapp:jwt', JSON.stringify(decoded_jwt), ) }).catch((e) => console.log('login error', e, `${MY_API_URL}/login/`)) } return ( <SafeAreaView style={styles.container}> <!-- more page items in between --> <TouchableOpacity onPress={() => postLoginInformation()} > <Image source={require('../images/login/login_button.png')} /> </TouchableOpacity> </SafeAreaView> ) } export default Login
解决方案
问题根源
App.jsx中的useEffect未设置依赖数组,虽会触发无限循环,但核心问题是仅在组件挂载时执行一次,后续AsyncStorage中JWT变更时,不会重新触发该方法更新expired和consented状态。- 登录操作完成后,既未主动通知App组件更新状态,也未直接触发页面跳转。
具体修复步骤
1. 重构App组件的认证状态检查逻辑
将认证状态检查封装为独立方法,并通过路由参数传递给登录组件,以便登录成功后主动触发状态更新:
import React, { useEffect, useState } from 'react' import { AsyncStorage } from 'react-native' import moment from 'moment' import { NavigationContainer } from '@react-navigation/native' import { createNativeStackNavigator } from '@react-navigation/native-stack' import Login from './modules/Login' import Home from './modules/Home' import Nav from './modules/Nav' import Events from './modules/Events/Events' import ConsentForm from './modules/ConsentForm' export default function App() { const [expired, setExpired] = useState(true) const [consented, setConsented] = useState(false) const Stack = createNativeStackNavigator() // 封装认证状态检查方法 const checkAuthStatus = async () => { const value = await AsyncStorage.getItem('@myapp:jwt'); const parsedValue = JSON.parse(value) if(parsedValue == null){ setExpired(true) }else{ const now = moment() setExpired(now.isAfter(moment(parsedValue?.expiry))) setConsented(parsedValue?.consented) } } // 组件挂载时执行一次初始检查 useEffect(() => { checkAuthStatus() }, []) return ( <NavigationContainer> <Stack.Navigator> {!expired ? ( consented ? ( <> <Stack.Screen name="Home" component={Home} /> <Stack.Screen name="Nav" component={Nav} /> <Stack.Screen name="Events" component={Events} /> </> ): ( <> <Stack.Screen name="ConsentForm" component={ConsentForm} /> </> ) ) : ( <> <Stack.Screen name="Login" component={Login} // 将状态检查方法传递给登录组件 initialParams={{ onLoginSuccess: checkAuthStatus }} /> </> )} </Stack.Navigator> </NavigationContainer> ) }
2. 修改登录页面,完成登录后主动更新状态并跳转
import React from 'react' import { SafeAreaView, TouchableOpacity, Image, AsyncStorage} from 'react-native' import jwt_decode from 'jwt-decode' import { MY_API_URL } from './constants' const Login = ({ navigation, route }) => { const [username, onChangeUsername] = React.useState(null) const [password, onChangePassword] = React.useState(null) // 获取App组件传递的状态更新方法 const { onLoginSuccess } = route.params const postLoginInformation = async () => { try { const res = await fetch(`${MY_API_URL}/login/`, { method: 'POST', headers: { Accept: 'application/json', 'Content-Type': 'application/json', }, body: JSON.stringify({ username: username, password: password, }), }) const response = await res.json() // 修正:response已是JSON格式,无需再次解析 const jwt = response.jwt const decoded_jwt = jwt_decode(jwt) await AsyncStorage.setItem( '@myapp:jwt', JSON.stringify(decoded_jwt), ) // 先更新App的认证状态 await onLoginSuccess() // 跳转到首页 navigation.navigate('Home') } catch (e) { console.log('login error', e, `${MY_API_URL}/login/`) } } return ( <SafeAreaView style={styles.container}> <!-- more page items in between --> <TouchableOpacity onPress={postLoginInformation}> <Image source={require('../images/login/login_button.png')} /> </TouchableOpacity> </SafeAreaView> ) } export default Login
3. 注销功能的修复思路
注销时删除AsyncStorage中的JWT,调用状态更新方法后跳转到登录页:
// 在需要注销的组件中(如Home/Nav) const handleLogout = async () => { await AsyncStorage.removeItem('@myapp:jwt') // 跳转到登录页,App组件会在路由切换后(或通过传递的方法)重新检查状态 navigation.navigate('Login') }
额外优化建议
- 使用React Context管理全局认证状态,避免通过路由参数传递方法,提升代码可维护性。
- 登录时添加加载状态,防止重复点击。
- 增加JWT解析失败的异常处理逻辑。
内容的提问来源于stack exchange,提问作者ellen
相关产品推荐
相关产品推荐

