You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Native StackNavigator登出报错,求非逐层传props解决方案

解决React Native中JWT登出导航报错问题

核心问题

  1. 登出时调用navigation.reset触发报错:The action 'RESET' with payload {"index":0,"routes":[{"name":"Login"}],"key":null} was not handled by any navigator
  2. 清除AsyncStorage中的Token后,App无法自动感知状态变化,导航栈未同步更新
  3. 需避免逐层传递setValidToken/setConsented props

解决方案:完善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;

报错原因说明

  1. 路由不存在:之前的代码中,loggedIn为true时Login页面未被加入导航栈,调用navigation.reset跳转时导航器找不到目标路由
  2. 状态不同步:登出时仅清除了AsyncStorage,但未更新App状态,导致导航栈仍显示登录后的页面,跳转操作自然失败

内容的提问来源于stack exchange,提问作者ellen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 08:55:56