如何在非组件文件中延迟API逻辑至Redux Persist数据复现后执行?
解决Redux Persist持久化完成前API服务无法获取Token的问题
下面是几种在非组件文件中处理Redux Persist重hydration完成前API依赖Token的方案:
方案一:通过persistStore回调初始化API服务
利用Redux Persist的persistStore提供的完成回调,在持久化数据恢复后再初始化API服务,避免提前执行无Token的请求。
1. 修改Store配置文件
// store.js import { configureStore } from '@reduxjs/toolkit'; import { persistStore, persistReducer } from 'redux-persist'; import storage from 'redux-persist/lib/storage'; import authSlice from './authSlice'; const persistConfig = { key: 'root', storage, whitelist: ['auth'], // 仅持久化包含token的auth切片 }; const persistedReducer = persistReducer(persistConfig, authSlice); export const store = configureStore({ reducer: { auth: persistedReducer, // 加入其他reducer和RTK Query的api reducer }, }); // 创建persistor并监听重hydration完成事件 export const persistor = persistStore(store, null, () => { // 完成后触发API服务初始化 import('./apiService').then(module => module.initializeApi()); });
2. 编写API服务文件
// apiService.js import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'; import { store } from './store'; let apiInstance = null; // 动态获取Token的基础查询函数 const baseQuery = fetchBaseQuery({ baseUrl: '/api', prepareHeaders: (headers) => { const token = store.getState().auth.accessToken; if (token) { headers.set('Authorization', `Bearer ${token}`); } return headers; }, }); // 初始化API实例的方法 export const initializeApi = () => { apiInstance = createApi({ reducerPath: 'api', baseQuery, endpoints: (builder) => ({ // 定义你的API端点 fetchData: builder.query({ query: () => '/data', }), }), }); // 导出组件用的hooks(如果需要) export const { useFetchDataQuery } = apiInstance; }; // 非组件环境下的请求方法,确保API已初始化 export const fetchDataInNonComponent = async () => { if (!apiInstance) { throw new Error('API未初始化,请等待持久化数据恢复完成'); } const result = await apiInstance.endpoints.fetchData.initiate()(store.dispatch); return result.data; };
方案二:监听Store的重hydration状态
在Auth切片中添加isRehydrated状态标记,API服务通过监听该状态,等待持久化完成后再执行请求。
1. 更新Auth切片
// authSlice.js import { createSlice } from '@reduxjs/toolkit'; const initialState = { accessToken: null, isRehydrated: false, // 标记是否完成重hydration }; const authSlice = createSlice({ name: 'auth', initialState, reducers: { setRehydrated: (state) => { state.isRehydrated = true; }, // 其他Auth相关的reducer }, extraReducers: (builder) => { // 监听Redux Persist的重hydration完成action builder.addCase('persist/REHYDRATE', (state, action) => { if (action.payload?.auth) { state.accessToken = action.payload.auth.accessToken; } state.isRehydrated = true; }); }, }); export const { setRehydrated } = authSlice.actions; export default authSlice.reducer;
2. 修改API服务文件
// apiService.js import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'; import { store } from './store'; // 等待重hydration完成的工具函数 const waitForRehydration = async () => { return new Promise((resolve) => { const unsubscribe = store.subscribe(() => { const { isRehydrated } = store.getState().auth; if (isRehydrated) { unsubscribe(); resolve(); } }); // 检查当前状态是否已完成,避免不必要的等待 if (store.getState().auth.isRehydrated) { unsubscribe(); resolve(); } }); }; const baseQuery = fetchBaseQuery({ baseUrl: '/api', prepareHeaders: async (headers) => { // 先等待持久化完成 await waitForRehydration(); const token = store.getState().auth.accessToken; if (token) { headers.set('Authorization', `Bearer ${token}`); } return headers; }, }); export const api = createApi({ reducerPath: 'api', baseQuery, endpoints: (builder) => ({ fetchData: builder.query({ query: () => '/data', }), }), }); export const { useFetchDataQuery } = api; // 非组件环境下的请求示例 export const fetchDataNonComponent = async () => { await waitForRehydration(); const result = await api.endpoints.fetchData.initiate()(store.dispatch); return result.data; };
方案三:在RTK Query中添加重试逻辑
如果首次请求因无Token失败,自动等待重hydration完成后重试请求,无需提前初始化API。
// apiService.js import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'; import { store } from './store'; const baseQuery = fetchBaseQuery({ baseUrl: '/api', prepareHeaders: (headers) => { const token = store.getState().auth.accessToken; if (token) { headers.set('Authorization', `Bearer ${token}`); } return headers; }, }); // 包装基础查询,添加重hydration重试逻辑 const baseQueryWithRehydrationRetry = async (args, api, extraOptions) => { let result = await baseQuery(args, api, extraOptions); // 若请求因未授权失败且未完成重hydration,等待后重试 if (result.error?.status === 401 && !store.getState().auth.isRehydrated) { await new Promise(resolve => { const unsubscribe = store.subscribe(() => { if (store.getState().auth.isRehydrated) { unsubscribe(); resolve(); } }); }); // 重试请求 result = await baseQuery(args, api, extraOptions); } return result; }; export const api = createApi({ reducerPath: 'api', baseQuery: baseQueryWithRehydrationRetry, endpoints: (builder) => ({ fetchData: builder.query({ query: () => '/data', }), }), }); export const { useFetchDataQuery } = api;
内容的提问来源于stack exchange,提问作者Abdul Aziz
相关产品推荐
相关产品推荐

