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

React Native使用SecureStore保存JWT时首次登录无法存储的问题求助

React Native使用SecureStore保存JWT时首次登录无法存储的问题求助

嗨,我遇到了一个登录时保存JWT的奇怪问题:第一次点击登录按钮时,JWT没能成功保存到SecureStore里,但第二次点击按钮就能正常存储了。我写了获取、保存JWT的函数,还有一个调试用的函数,但实在搞不懂为什么会出现这种情况,有没有大佬能帮忙看看怎么解决?

我的LoginScreen代码如下:

import { Button, KeyboardAvoidingView, StyleSheet, Text, TextInput, TouchableOpacity, View } from 'react-native'
import React, { useEffect, useState } from 'react'
import { auth } from '../firebase'
import { onAuthStateChanged, signInWithEmailAndPassword } from 'firebase/auth'
import { useNavigation } from '@react-navigation/native'
import * as SecureStore from 'expo-secure-store';

const LoginScreen = () => {
  const [token, setToken] = useState('')
  const [email, setEmail] = useState('')
  const [password, setPassword] = useState('')
  const [error, setError] = useState('')
  const navigation = useNavigation();

  async function getToken(){
    try {
      const user = auth.currentUser;
      if (user) {
        const idTokenResult = await user.getIdTokenResult();
        setToken(idTokenResult.token);
        console.log('token set')
      }
    } catch (error) {
      console.log(error);
    }
  }

  async function saveToken(jwt, token) {
    await SecureStore.setItemAsync(jwt, token);
  }

  async function getValueFor(key) {
    let result = await SecureStore.getItemAsync(key);
    if (result) {
      console.log("🔍 Here's your value in LoginScreen 🔍 \n" + result);
    } else {
      console.log('No values stored under that key.');
    }
  }

  useEffect(() => {
    const unsubscribe = onAuthStateChanged(auth, user => {
      if (user){
        console.log('here')
      }
    })
    // unsubscribe va stopper le listner firebase lorsque l'utilisateur sort de la page login
    return unsubscribe
  }, [])

  const handleLogin = async () => {
    await signInWithEmailAndPassword(auth, email, password)
    .then(userCredentials => {
      const user = userCredentials.user;
      getToken()
      console.log('firebase : logged in')
    })
    .catch(error => setError(error.message))
    saveToken('jwt', token);
    getValueFor('jwt');
  }

  return (
    <KeyboardAvoidingView
      style={styles.container}
      behavior="padding">
      <View style={styles.inputContainer}>
        {error ? <Text style={{ color: 'red' }}>{error}</Text> : null}
        <TextInput
          placeholder='Identifiant'
          value={email}
          onChangeText={text => setEmail(text)}
          style={styles.input}
        />
        <TextInput
          placeholder='Mot de passe'
          value={password}
          onChangeText={text => setPassword(text)}
          style={styles.input}
          secureTextEntry
        />
      </View>
      <View style={styles.textView}>
        <TouchableOpacity style={styles.text} onPress={() => navigation.navigate('ForgotPassword')}>
          <Text>Mot de passe oublié ?</Text>
        </TouchableOpacity>
        <TouchableOpacity style={styles.text} onPress={() => navigation.navigate('Register')}>
          <Text>Pas encore de compte ? Inscris-toi ici.</Text>
        </TouchableOpacity>
      </View>
      <View
        style={styles.buttonContainer}>
        <TouchableOpacity
          onPress={handleLogin}
          style={styles.button}
        >
          <Text style={styles.buttonText}>Connexion</Text>
        </TouchableOpacity>
      </View>
    </KeyboardAvoidingView>
  )
}

export default LoginScreen

首次点击登录按钮时,终端输出显示getValueFor函数打印了No values stored under that key.,但第二次点击就能正常读出保存的JWT。


问题分析&解决方案

其实核心问题是React状态更新的异步特性,加上你的异步流程没处理到位:

  1. 调用signInWithEmailAndPassword成功后,then里调用getToken,getToken里的setToken是异步更新状态的——这意味着执行完getToken后,token并不会立刻变成新值,还是初始的空字符串。
  2. 你紧接着就执行了saveToken('jwt', token),此时存的是空值,所以调试函数查不到内容;第二次点击时,token已经是上一次getToken设置好的值,所以能成功保存。

给你几个可行的修复方案:

方案一:直接在getToken里保存token(不依赖state)

修改getToken函数,获取到token后直接调用保存方法,跳过state更新的异步等待:

async function getToken(){
  try {
    const user = auth.currentUser;
    if (user) {
      const idTokenResult = await user.getIdTokenResult();
      const currentToken = idTokenResult.token;
      setToken(currentToken);
      // 直接用获取到的token保存,不用等state更新
      await saveToken('jwt', currentToken);
      await getValueFor('jwt');
      console.log('token set and saved')
    }
  } catch (error) {
    console.log(error);
  }
}

然后修改handleLogin,去掉后面多余的保存和调试代码:

const handleLogin = async () => {
  try {
    const userCredentials = await signInWithEmailAndPassword(auth, email, password);
    await getToken();
    console.log('firebase : logged in')
  } catch(error) {
    setError(error.message)
  }
}

方案二:用useEffect监听token变化自动保存

如果想保留state的依赖,可以添加一个监听token变化的useEffect,状态更新后自动触发保存:

// 新增这个useEffect
useEffect(() => {
  if (token) {
    saveToken('jwt', token);
    getValueFor('jwt');
  }
}, [token])

// 修改handleLogin,确保等待getToken执行完成
const handleLogin = async () => {
  try {
    const userCredentials = await signInWithEmailAndPassword(auth, email, password);
    await getToken();
    console.log('firebase : logged in')
  } catch(error) {
    setError(error.message)
  }
}

备注:内容来源于stack exchange,提问作者Guigz

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.22 16:03:06