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

如何在非组件文件中延迟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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 16:01:10