为何createAsyncThunk无法自动传递extraArgument类型?
解决方案
1. 全局定义Thunk Extra参数类型(简化重复类型声明)
在项目类型定义文件或共享库的类型文件中,扩展Redux Toolkit的ThunkExtraArgument全局类型,让所有createAsyncThunk自动继承该类型,无需每个thunk都显式传递第三个泛型参数。
// types/redux.d.ts(或共享库的类型文件) import type { AxiosInstance } from 'axios'; import type { ThunkExtraArgument } from '@reduxjs/toolkit'; declare module '@reduxjs/toolkit' { interface ThunkExtraArgument { serviceApi: AxiosInstance; } }
之后编写createAsyncThunk时,可直接省略ExtraArgument泛型,extra参数会自动获得类型提示:
// devices.ts(共享库中的slice文件) import { createAsyncThunk } from '@reduxjs/toolkit'; import type { Device } from './types'; interface FetchResultFromServer { count: number; entities: Device[]; } // 无需显式传递ExtraArgument类型 export const fetchDevices = createAsyncThunk<FetchResultFromServer, void>( 'devices/fetch', async (_, { extra }) => { // extra.serviceApi 自动带有AxiosInstance类型 const response = await extra.serviceApi.get('/devices'); return response.data; } );
2. 消除Store与Slice的依赖(支持共享库迁移)
核心采用依赖倒置思想:slice仅定义业务逻辑和数据结构,不依赖具体API实例;API实例由Store配置时通过Thunk的extra参数注入,实现slice与Store的完全解耦,可直接迁移至共享库。
具体实现:
- 将slice的reducer、异步thunk、action全部放入共享库,仅暴露reducer和action,不引入任何外部依赖(如axios实例)。
- 主项目的Store配置中,注入具体的axios实例到Thunk的extra参数,保持Store与slice的独立。
// 共享库中的devices.slice.ts import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'; import type { Device } from './types'; interface FetchResultFromServer { count: number; entities: Device[]; } // 异步thunk完全依赖extra参数注入的API export const fetchDevices = createAsyncThunk<FetchResultFromServer, void>( 'devices/fetch', async (_, { extra }) => { const response = await extra.serviceApi.get('/devices'); return response.data; } ); interface DevicesState { list: Device[]; loading: boolean; error: string | null; } const initialState: DevicesState = { list: [], loading: false, error: null, }; const devicesSlice = createSlice({ name: 'devices', initialState, reducers: {}, extraReducers: (builder) => { builder .addCase(fetchDevices.pending, (state) => { state.loading = true; state.error = null; }) .addCase(fetchDevices.fulfilled, (state, action) => { state.loading = false; state.list = action.payload.entities; }) .addCase(fetchDevices.rejected, (state, action) => { state.loading = false; state.error = action.error.message || '获取设备列表失败'; }); }, }); export default devicesSlice.reducer;
// 主项目的store.ts import { configureStore } from '@reduxjs/toolkit'; import axios from 'axios'; import devicesReducer from '@your-shared-lib/devices.slice'; // 创建具体的axios实例 const axiosInstance = axios.create({ baseURL: 'https://your-api-domain.com', headers: { 'X-API-KEY': 'your-api-key' }, }); const store = configureStore({ middleware: (getDefaultMiddleware) => getDefaultMiddleware({ thunk: { extraArgument: { serviceApi: axiosInstance, // 注入API实例 }, }, }), reducer: { devices: devicesReducer, // 引入共享库的reducer }, }); export type RootState = ReturnType<typeof store.getState>; export type AppDispatch = typeof store.dispatch;
3. 可选优化:封装自定义Thunk Creator
如果需要更统一的类型管理或公共逻辑处理,可封装自定义的createAppAsyncThunk函数,进一步简化代码:
// 共享库中的utils/createAppAsyncThunk.ts import { createAsyncThunk } from '@reduxjs/toolkit'; import type { ThunkExtraArgument } from '@reduxjs/toolkit'; // 封装自定义thunk creator,自动携带extra类型 export const createAppAsyncThunk = createAsyncThunk.withTypes<{ state: unknown; // 可根据需求自定义,或留空由主项目扩展 extra: ThunkExtraArgument; rejectValue: string; // 统一错误类型,可选 }>();
使用示例:
// 共享库中的devices.slice.ts import { createAppAsyncThunk } from './utils/createAppAsyncThunk'; export const fetchDevices = createAppAsyncThunk<FetchResultFromServer, void>( 'devices/fetch', async (_, { extra }) => { const response = await extra.serviceApi.get('/devices'); return response.data; } );
内容的提问来源于stack exchange,提问作者yevt
相关产品推荐
相关产品推荐

