如何使用useInfiniteQuery处理多查询与游标分页请求?
多接口无限游标加载的解决方案
针对你遇到的「需要同时处理多个带游标分页的接口,useInfiniteQuery不支持批量查询、useQueries无法适配无限加载」的问题,这里提供几个实用的解决思路:
方案一:用useQueries批量管理多个useInfiniteQuery实例
useQueries本身支持传入多个查询配置对象,每个对象可以完全按照useInfiniteQuery的参数来配置,包括getNextPageParam、fetchNextPage等无限加载相关选项,以此一次性创建多个独立的无限查询实例。
示例代码:
import { useQueries } from '@tanstack/react-query'; // 定义每个接口的查询配置 const queryConfigs = [ { queryKey: ['projects'], queryFn: fetchProjects, getNextPageParam: (lastPage) => lastPage.nextCursor, }, { queryKey: ['tasks'], queryFn: fetchTasks, getNextPageParam: (lastPage) => lastPage.nextCursor, }, // 更多接口配置... ]; // 用useQueries批量创建无限查询 const queryResults = useQueries({ queries: queryConfigs, }); // 每个结果对应一个接口的无限查询状态 const [projectsQuery, tasksQuery] = queryResults; // 调用单个接口的加载下一页方法 projectsQuery.fetchNextPage();
方案二:封装自定义Hook处理动态数量的无限查询
如果查询数量是动态的(比如从props传入),可以封装自定义Hook,内部通过useQueries动态生成查询配置,避免直接在组件map回调中调用Hook的问题。
示例代码:
import { useQueries } from '@tanstack/react-query'; function useMultiInfiniteQueries(queryConfigs) { return useQueries({ queries: queryConfigs.map(config => ({ ...config, queryKey: config.queryKey, getNextPageParam: config.getNextPageParam, })), }); } // 在组件中使用 function MyComponent({ resources }) { // resources是动态接口列表,格式示例:[{key: 'projects', fetchFn: fetchProjects}, ...] const queryConfigs = resources.map(resource => ({ queryKey: [resource.key], queryFn: resource.fetchFn, getNextPageParam: (lastPage) => lastPage.nextCursor, })); const queryResults = useMultiInfiniteQueries(queryConfigs); // 遍历结果处理每个接口的加载逻辑 return ( <div> {queryResults.map((result, index) => ( <div key={resources[index].key}> {result.data?.pages.map(page => ( <div key={page.nextCursor}>{/* 渲染数据 */}</div> ))} {result.hasNextPage && ( <button onClick={() => result.fetchNextPage()} disabled={result.isFetchingNextPage}> 加载更多 </button> )} </div> ))} </div> ); }
方案三:拆分组件,每个接口对应独立子组件
如果动态查询数量较多,且每个接口的渲染逻辑独立,可以把单个无限查询的逻辑封装到子组件中,在父组件里通过map渲染子组件,这样每个子组件内部的useInfiniteQuery都在组件顶层调用,完全符合Hook规则。
示例代码:
// 子组件:单个接口的无限加载逻辑 function InfiniteResource({ resourceKey, fetchFn }) { const { data, hasNextPage, fetchNextPage, isFetchingNextPage } = useInfiniteQuery({ queryKey: [resourceKey], queryFn: fetchFn, getNextPageParam: (lastPage) => lastPage.nextCursor, }); return ( <div> {data?.pages.map(page => ( <div key={page.nextCursor}>{/* 渲染当前页数据 */}</div> ))} {hasNextPage && ( <button onClick={() => fetchNextPage()} disabled={isFetchingNextPage}> 加载更多 </button> )} </div> ); } // 父组件 function ParentComponent({ resources }) { return ( <div> {resources.map(resource => ( <InfiniteResource key={resource.key} resourceKey={resource.key} fetchFn={resource.fetchFn} /> ))} </div> ); }
以上三种方案可根据业务场景选择:若需统一管理所有查询状态,优先选方案一或二;若每个接口的渲染和加载逻辑独立,方案三的组件拆分更清晰。
内容的提问来源于stack exchange,提问作者Arp
相关产品推荐
相关产品推荐

