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状态更新的异步特性,加上你的异步流程没处理到位:
- 调用
signInWithEmailAndPassword成功后,then里调用getToken,getToken里的setToken是异步更新状态的——这意味着执行完getToken后,token并不会立刻变成新值,还是初始的空字符串。 - 你紧接着就执行了
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
相关产品推荐
相关产品推荐

