React Native:解决刷新/重启App时Redux获取AsyncStorage Token为undefined
问题分析与解决方案
问题根源
你遇到的核心问题是异步操作的时序问题:
getData()是异步函数,调用后会立即返回一个Promise,不会阻塞后续代码执行。- 当你执行
console.log(savedToken)和创建initialState时,await AsyncStorage.getItem('token')还未完成,savedToken仍保持初始的undefined值。 - 虽然
getData内部能打印到Token,但这是在异步操作完成后才执行的,此时initialState已经创建完毕,无法再被更新。
正确实现方式
我们可以通过Redux Toolkit的createAsyncThunk来异步加载本地存储的Token,并在App启动时触发这个操作,确保Token被正确加载到Redux状态中。
步骤1:创建加载Token的异步Thunk
在你的user slice文件中,添加一个异步Thunk用于从AsyncStorage获取Token:
import { createSlice, createAsyncThunk, PayloadAction } from '@reduxjs/toolkit'; import { User, LoginPayload, LoginPayloadError } from './interface'; import { login } from '../../service/authService'; import AsyncStorage from '@react-native-async-storage/async-storage'; // 定义异步Thunk,用于加载本地存储的Token export const loadSavedToken = createAsyncThunk( 'user/loadSavedToken', async () => { const token = await AsyncStorage.getItem('token'); return token || ''; // 如果没有Token则返回空字符串 } );
步骤2:修改Slice的初始状态与Reducer
调整slice的初始状态,并在extraReducers中处理loadSavedToken的完成状态:
const initialState: User = { token: '', // 初始值设为空,后续通过异步操作更新 id: 0, email: '', nicename: '', firstName: '', lastName: '', displayName: '', role: '', loading: false, error: '', }; const userSlice = createSlice({ name: 'user', initialState, reducers: {}, extraReducers: builder => { builder // 处理加载Token的完成状态 .addCase(loadSavedToken.fulfilled, (state, action: PayloadAction<string>) => { state.token = action.payload; }) // 处理登录请求中状态 .addCase(login.pending, (state) => { state.loading = true; }) // 处理登录成功状态 .addCase(login.fulfilled, (state, action: PayloadAction<User>) => { // 直接存储Token到AsyncStorage,无需包裹额外函数 AsyncStorage.setItem('token', action.payload.token); // 直接修改state(RTK内置Immer支持不可变更新) state.loading = false; state.token = action.payload.token; state.id = action.payload.id; state.email = action.payload.email; state.nicename = action.payload.nicename; state.firstName = action.payload.firstName; state.lastName = action.payload.lastName; state.displayName = action.payload.displayName; state.role = action.payload.role; }) // 处理登录失败状态 .addCase(login.rejected, (state, action) => { state.loading = false; state.error = action.payload as LoginPayloadError; }); }, }); export default userSlice.reducer;
步骤3:在App启动时触发Token加载
在你的根组件(如App.tsx或App.js)中,使用useEffect和useDispatch触发loadSavedToken:
import { useDispatch } from 'react-redux'; import { useEffect } from 'react'; import { loadSavedToken } from './path/to/userSlice'; function App() { const dispatch = useDispatch(); useEffect(() => { // App启动时加载本地Token dispatch(loadSavedToken()); }, [dispatch]); // 其他组件逻辑... } export default App;
额外优化点
- 避免在
login.fulfilled中返回新的state对象:RTK内置Immer库,允许直接修改state,代码更简洁且符合规范。 - 不需要为
AsyncStorage.setItem单独定义函数:直接调用即可,这是异步副作用操作,不影响Redux状态的同步更新。
内容的提问来源于stack exchange,提问作者Shiva Giri
相关产品推荐
相关产品推荐

