如何将两个React Query接口请求结果合并到同一查询缓存中?
React Query 合并两个分页接口缓存到同一键的方案
要实现将两个接口的分页数据合并到同一个查询缓存键下,同时避免覆盖或请求被阻止的问题,核心思路是用不同的查询键触发各自请求,再手动合并数据到目标缓存,具体方案如下:
1. 为两个接口设置独立的查询键
给两个分页接口分配带标识的唯一查询键(包含分页参数),确保各自的请求能正常触发,不会被对方的缓存拦截:
import { useQuery, useQueryClient } from '@tanstack/react-query'; // 获取全部记录的分页查询 const useAllRecords = (page) => { return useQuery( ["records", "all", page], // 唯一键:主标识+类型+分页 () => fetch(`/records/?page=${page}`).then(res => res.json()), { keepPreviousData: true } // 分页切换时保留上一页数据,避免空白 ); }; // 获取未关联所有者记录的分页查询 const useUnownedRecords = (page) => { const queryClient = useQueryClient(); return useQuery( ["records", "unowned", page], // 唯一键:主标识+类型+分页 () => fetch(`/records/unowned?page=${page}`).then(res => res.json()), { keepPreviousData: true, // 请求成功后合并数据到目标缓存 onSuccess: (newPageData) => { // 读取现有合并缓存 const existingMerged = queryClient.getQueryData(["records"]) || []; // 合并新数据并去重(假设记录有唯一id字段) const merged = [...existingMerged, ...newPageData].filter( (item, index, self) => index === self.findIndex(r => r.id === item.id) ); // 更新目标缓存 queryClient.setQueryData(["records"], merged); } } ); };
2. 在组件中使用并读取合并后的缓存
在业务组件中调用两个分页查询,直接通过queryClient.getQueryData获取合并后的全部记录:
import { useState } from 'react'; import { useQueryClient } from '@tanstack/react-query'; const RecordList = () => { const queryClient = useQueryClient(); const [allPage, setAllPage] = useState(1); const [unownedPage, setUnownedPage] = useState(1); // 触发两个分页请求 const { isLoading: isLoadingAll } = useAllRecords(allPage); const { isLoading: isLoadingUnowned } = useUnownedRecords(unownedPage); // 获取合并后的全部记录 const mergedRecords = queryClient.getQueryData(["records"]) || []; return ( <div> <button onClick={() => setAllPage(prev => prev + 1)} disabled={isLoadingAll} >加载更多全部记录</button> <button onClick={() => setUnownedPage(prev => prev + 1)} disabled={isLoadingUnowned} >加载更多未关联记录</button> <ul> {mergedRecords.map(record => ( <li key={record.id}>{record.title}</li> ))} </ul> </div> ); };
3. 进阶:自动同步合并缓存(可选)
如果希望合并后的缓存能自动监听两个子查询的更新,可封装一个专门的合并查询hook:
const useMergedRecords = () => { const queryClient = useQueryClient(); return useQuery(["records"], () => { // 读取所有已缓存的全部记录分页数据 const allPages = queryClient.getQueriesData(["records", "all"]) .flatMap(([_, data]) => data || []); // 读取所有已缓存的未关联记录分页数据 const unownedPages = queryClient.getQueriesData(["records", "unowned"]) .flatMap(([_, data]) => data || []); // 合并并去重 return [...new Map([...allPages, ...unownedPages].map(r => [r.id, r])).values()]; }, { // 通过子查询的更新时间戳触发缓存刷新 queryKeyHashFn: () => { const allUpdateTimes = queryClient.getQueryCache().getAll() .filter(q => q.queryKey[0] === "records" && q.queryKey[1] === "all") .map(q => q.state.dataUpdatedAt); const unownedUpdateTimes = queryClient.getQueryCache().getAll() .filter(q => q.queryKey[0] === "records" && q.queryKey[1] === "unowned") .map(q => q.state.dataUpdatedAt); return JSON.stringify([...allUpdateTimes, ...unownedUpdateTimes]); }, refetchInterval: false }); }; // 使用方式 const { data: mergedRecords } = useMergedRecords();
关键注意点
- 去重逻辑:必须添加去重处理,避免两个接口返回重复记录导致缓存冗余。
- 分页参数:子查询的键必须包含分页参数,确保每一页的请求都能被单独缓存,不会互相覆盖。
- 缓存失效:如果后续需要更新合并后的缓存,可调用
queryClient.invalidateQueries(["records"])触发重新合并。
内容的提问来源于stack exchange,提问作者ABMagil
相关产品推荐
相关产品推荐

