FetchBaseQuery 独立组件场景如何失效缓存触发数据重取
问题背景
同页面挂载两个无父子层级关系的独立组件,需求如下:
- 组件1:发起API调用获取记录数据,渲染支持服务端分页的表格
- 组件2:承载表单,用户提交表单后,需要组件1重新向后端拉取最新数据
当前基于fetchBaseQuery实现数据请求,尝试通过缓存失效触发组件1接口重拉,以下方案均未生效:
- 调用
refetch()方法触发重拉 - 配置
keepUnusedDataFor设置缓存超时时间 - 尝试基于标签(tags)实现,但不确定是否需要用mutation替代query才能适配业务场景
现有项目代码
component1.tsx
let { data, error, isSuccess, isError, isFetching, refetch } = useGetQuery(request, { skip}); const records = data?.records; React.useEffect(() => { if (records) { // set records within table } }, [records]);
useGetQuery.ts
const extendedApi = mainApi.injectEndpoints({ endpoints: (builder) => ({ getQuery: builder.query<response, request>({ query: (request?: request) => ({ url: "someURL", body: request, method: "POST", }), providesTags: ["Requests"], }), }), overrideExisting: true, }); export const { useGetQuery } = extendedApi;
component2.tsx
let [trigger, data] = useSubmitFormMutation(); const submitForm = (e) => { e.preventDefault(); trigger(// Some Object); } React.useEffect(() => { if (isSuccess) { updateRefreshRecords(true); // setting the hook to true to make an API call in component 1 } }, [isSuccess]);
useSubmitFormMutation.ts
const extendedApi = mainApi.injectEndpoints({ endpoints: (builder) => ({ submitForm: builder.mutation<response, request>({ query: (request?: request) => ({ url: "some_other_url", body: request, method: "POST", }), invalidatesTags: ["Requests"], }), }), overrideExisting: false, }); export const { useSubmitFormMutation } = extendedApi;
mainAPI.ts
export const dynamicBaseQuery: BaseQueryFn<string | FetchArgs, unknown, FetchBaseQueryError> = async (args, api, extraOptions) => { const { mainApiUrl } = (api.getState() as RootState).settings.endpoints; const rawBaseQuery = fetchBaseQuery({ baseUrl: mainApiUrl, prepareHeaders: (headers, { getState }) => { // Use getState to pull the jwtToken and pass it in the headers to the api endpoint. const { jwtToken } = (getState() as RootState).auth; headers.set("authorization", jwtToken); return headers; }, }); return rawBaseQuery(args, api, extraOptions); }; export const mainApi = createApi({ reducerPath: "mainApi", baseQuery: dynamicBaseQuery, endpoints: () => ({}), tagTypes: ["Requests"], });
store.ts
export const store = configureStore({ reducer: { // other reducers [localApi.reducerPath]: localApi.reducer, [mainApi.reducerPath]: mainApi.reducer, }, middleware: (getDefaultMiddleware) => getDefaultMiddleware({ serializableCheck: false, }) .concat(localApi.middleware) .concat(mainApi.middleware), });
修复方案
你当前的标签型缓存失效思路是对的,不需要把列表查询接口改成mutation——query本身就是用于读数据场景,用POST方法发起查询请求完全符合RTK Query设计,标签失效逻辑对POST类型的query同样生效。之前方案不生效是两个代码错误导致的,按以下步骤修改即可:
- 修正组件2的mutation状态解构错误
你当前的代码没有从useSubmitFormMutation返回值中解构出isSuccess状态,判断逻辑用的是未定义的游离变量,永远不会进入成功分支。修改hook解构逻辑:// 补全解构mutation的加载、成功状态 const [trigger, { isSuccess }] = useSubmitFormMutation(); - 修正列表query的标签绑定逻辑
因为你的列表是服务端分页,每次请求带的分页、筛选参数不同,会生成独立的缓存条目,将providesTags改为函数返回形式,确保所有参数对应的缓存条目都能正确绑定Requests标签:getQuery: builder.query<response, request>({ query: (request?: request) => ({ url: "someURL", body: request, method: "POST", }), // 改为函数形式,无论请求参数是什么,都给当前缓存条目打上Requests标签 providesTags: () => ["Requests"], }), - 删除多余的跨组件状态触发逻辑
不需要手动维护updateRefreshRecords这类全局状态做跨组件通信,只要标签绑定正确,mutation完成后触发invalidatesTags时,RTK Query会自动找到所有绑定了Requests标签的活跃query缓存,自动失效并重新发起请求拉取最新数据。组件2中监听isSuccess调用updateRefreshRecords的useEffect可以直接删掉。
之前方案失效的原因
- 直接调用
refetch不可行:两个组件是兄弟关系,组件2拿不到组件1内部hook返回的refetch方法引用 keepUnusedDataFor控制的是未挂载组件的缓存保留时间,不会主动触发已挂载组件的重拉- 初始标签方案失效:一是组件2根本没正确拿到表单提交成功的状态,二是动态参数的query缓存没有正确绑定标签,导致失效逻辑没有命中目标缓存
内容的提问来源于stack exchange,提问作者Diksha Goyal
相关产品推荐
相关产品推荐

