React Native StackNavigator登出报错,求非逐层传props解决方案
解决React Native中JWT登出导航报错问题
核心问题
- 登出时调用
navigation.reset触发报错:The action 'RESET' with payload {"index":0,"routes":[{"name":"Login"}],"key":null} was not handled by any navigator - 清除AsyncStorage中的Token后,App无法自动感知状态变化,导航栈未同步更新
- 需避免逐层传递
setValidToken/setConsentedprops
解决方案:完善AuthContext状态管理,同步状态与导航
1. 重构AuthContext,封装认证逻辑
将登录、登出、状态初始化逻辑统一封装在Context中,避免组件重复处理存储与状态:
import { createContext, useContext, useState, useEffect } from "react"; import AsyncStorage from '@react-native-async-storage/async-storage'; import moment from 'moment'; const AuthContext = createContext(); export const AuthProvider = ({ children }) => { const [consented, setConsented] = useState(false); const [loggedIn, setLoggedIn] = useState(false); // 初始化读取存储的JWT,设置认证状态 const initAuthState = async () => { try { const value = await AsyncStorage.getItem('@myapp:jwt'); if (value) { const decodedJwt = JSON.parse(value); const now = moment(); setLoggedIn(!now.isAfter(moment(decodedJwt?.expiry))); setConsented(decodedJwt?.consented); } else { setLoggedIn(false); setConsented(false); } } catch (e) { console.error('初始化认证状态失败:', e); setLoggedIn(false); setConsented(false); } }; // 登出:清除存储+同步更新状态 const logOut = async () => { try { const keys = await AsyncStorage.getAllKeys(); await AsyncStorage.multiRemove(keys); setLoggedIn(false); setConsented(false); } catch (e) { console.error('登出失败:', e); } }; // 登录成功后更新状态 const loginSuccess = (decodedJwt) => { setLoggedIn(true); setConsented(decodedJwt?.consented); }; useEffect(() => { initAuthState(); }, []); return ( <AuthContext.Provider value={{ loggedIn, consented, logOut, loginSuccess }}> {children} </AuthContext.Provider> ); }; // 自定义Hook简化上下文调用 export const useAuth = () => useContext(AuthContext);
2. 简化App.jsx导航配置
基于Context状态动态渲染导航栈,无需手动处理存储读取:
import React from 'react'; import { NavigationContainer } from '@react-navigation/native'; import { createNativeStackNavigator } from '@react-navigation/native-stack'; import { AuthProvider, useAuth } from './AuthContext'; import Home from './Home'; import Nav from './Nav'; import ConsentForm from './ConsentForm'; import Login from './Login'; const Stack = createNativeStackNavigator(); // 抽离导航组件,直接使用Context状态 const AppNavigator = () => { const { loggedIn, consented } = useAuth(); return ( <Stack.Navigator screenOptions={{ headerShown: false }}> {loggedIn ? ( consented ? ( <> <Stack.Screen name="Home" component={Home} /> <Stack.Screen name="Nav" component={Nav} /> </> ) : ( <Stack.Screen name="ConsentForm" component={ConsentForm} /> ) ) : ( <Stack.Screen name="Login" component={Login} /> )} </Stack.Navigator> ); }; export default function App() { return ( <AuthProvider> <NavigationContainer> <AppNavigator /> </NavigationContainer> </AuthProvider> ); }
3. 更新Login组件,使用Context的登录方法
import React, { useState } from 'react'; import { SafeAreaView, View, TextInput, TouchableOpacity, Image } from 'react-native'; import { useAuth } from '../AuthContext'; import jwt_decode from 'jwt-decode'; import AsyncStorage from '@react-native-async-storage/async-storage'; const Login = () => { const [username, onChangeUsername] = useState(null); const [password, onChangePassword] = useState(null); const { loginSuccess } = useAuth(); const MYAPP_URL = '你的API地址'; const postLoginInformation = async () => { try { const res = await fetch(`${MYAPP_URL}/login/`, { method: 'POST', headers: { Accept: 'application/json', 'Content-Type': 'application/json', }, body: JSON.stringify({ username, password }), }); const response = await res.json(); const jwt = response.jwt; const decodedJwt = jwt_decode(jwt); await AsyncStorage.setItem('@myapp:jwt', JSON.stringify(decodedJwt)); loginSuccess(decodedJwt); } catch (e) { console.log('登录失败:', e, `${MYAPP_URL}/login/`); } }; return ( <SafeAreaView style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}> <View> <TextInput style={{ width: 250, height: 40, borderWidth: 1, marginBottom: 10, padding: 5 }} onChangeText={onChangeUsername} value={username} placeholder="Username" /> <TextInput style={{ width: 250, height: 40, borderWidth: 1, marginBottom: 10, padding: 5 }} secureTextEntry onChangeText={onChangePassword} value={password} placeholder="Password" /> <TouchableOpacity style={{ height: 100 }} onPress={postLoginInformation}> <Image source={require('../images/login/login_button.png')} style={{ width: '100%', height: '100%' }} /> </TouchableOpacity> </View> </SafeAreaView> ); }; export default Login;
4. 简化Nav组件的登出逻辑
直接调用Context的logOut方法,无需手动处理导航跳转:
import React from 'react'; import { View, Button } from 'react-native'; import { useAuth } from './AuthContext'; const Nav = () => { const { logOut } = useAuth(); return ( <View> {/* 其他导航元素 */} <Button title="登出" onPress={logOut} /> </View> ); }; export default Nav;
报错原因说明
- 路由不存在:之前的代码中,
loggedIn为true时Login页面未被加入导航栈,调用navigation.reset跳转时导航器找不到目标路由 - 状态不同步:登出时仅清除了AsyncStorage,但未更新App状态,导致导航栈仍显示登录后的页面,跳转操作自然失败
内容的提问来源于stack exchange,提问作者ellen
相关产品推荐
相关产品推荐

