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

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
解决方案

问题根源

  1. App.jsx中的useEffect未设置依赖数组,虽会触发无限循环,但核心问题是仅在组件挂载时执行一次,后续AsyncStorage中JWT变更时,不会重新触发该方法更新expired和consented状态。
  2. 登录操作完成后,既未主动通知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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 04:16:02