RTK Query中能否传入服务函数?非序列化值报错求助
解决RTK Query传入非序列化服务实例导致的状态错误
我有个需要从React Hook获取用户token的服务,无法直接在Redux Toolkit Query(RTKQ)的API定义中使用。于是尝试在调用mutation时传入该服务实例,功能可以正常运行,但会收到「状态中存在非序列化数据」的错误日志。
代码示例
Mutation定义
removeItemById: builder.mutation<void, {service: ItemService; uuid: string}>({ invalidatesTags: ['Beneficiaries'], queryFn: async ({ service, uuid }) => { const result = await service.remove(uuid); return { data: result }; }, })
组件中调用
const [remove, { isLoading, isError, isSuccess }] = useRemoveItemByIdMutation(); const handleRemove = () => { remove({service: itemService, uuid}); };
错误信息
A non-serializable value was detected in the state, in the path:
item.queries.getItems({}).originalArgs.add
翻译:检测到状态中存在非序列化值,路径:item.queries.getItems({}).originalArgs.add
解决方法
问题根源
RTKQ会自动缓存mutation/query的调用参数(即originalArgs),而你传入的ItemService是类实例/复杂对象,属于Redux默认不允许的非序列化数据,因此触发了错误提示。
方法1:重构服务,避免传入实例
最推荐的方式是调整服务的设计,让它可以在RTKQ的queryFn内部直接调用,无需传入实例:
- 把服务的方法改成纯函数,将token作为参数传入
- 在
queryFn中从Redux store获取token(如果token存在store中),再调用服务方法
示例代码:
重构后的服务
// ItemService.ts export const removeItem = async (uuid: string, token: string) => { const response = await fetch(`/api/items/${uuid}`, { method: 'DELETE', headers: { 'Authorization': `Bearer ${token}` } }); return response.json(); };
更新后的RTKQ Mutation
removeItemById: builder.mutation<void, string>({ invalidatesTags: ['Beneficiaries'], queryFn: async (uuid, { getState }) => { // 从store中获取token const token = (getState() as RootState).auth.token; const result = await removeItem(uuid, token); return { data: result }; }, })
组件调用简化
const [remove, { isLoading, isError, isSuccess }] = useRemoveItemByIdMutation(); const handleRemove = () => { remove(uuid); };
方法2:忽略特定路径的非序列化检测(不推荐)
如果暂时无法重构服务,可以在Redux配置中忽略RTKQ缓存参数的序列化检测,治标不治本,仅作为临时方案:
export const store = configureStore({ reducer: { [api.reducerPath]: api.reducer, // 其他reducer }, middleware: (getDefaultMiddleware) => getDefaultMiddleware({ serializableCheck: { ignoredActions: [api.util.resetApiState], // 忽略RTKQ的queries和mutations路径下的序列化检测 ignoredPaths: ['item.queries', 'item.mutations'], }, }).concat(api.middleware), });
方法3:用自定义baseQuery注入token
如果服务基于fetch/axios,可以通过RTKQ的baseQuery统一注入token,无需手动传参:
const baseQuery = fetchBaseQuery({ baseUrl: '/api', prepareHeaders: (headers, { getState }) => { const token = (getState() as RootState).auth.token; if (token) { headers.set('Authorization', `Bearer ${token}`); } return headers; }, }); // 之后在API定义中使用这个baseQuery export const api = createApi({ baseQuery, endpoints: (builder) => ({ /* ... */ }), });
内容的提问来源于stack exchange,提问作者DoctorHe
相关产品推荐
相关产品推荐

