如何结合带变量的GraphQL查询实现useSWRInfinite无限滚动
用useSWRInfinite实现GraphQL游标分页的无限滚动
问题背景
现有基于useSWR的GraphQL查询代码,可正常获取前10条链接,但需要实现无限滚动功能,希望改用useSWRInfinite,但不清楚如何结合带变量的GraphQL查询完成改造。
原useSWR实现代码:
const AllLinksQuery = gql` query allLinksQuery($first: Int, $after: String) { links(first: $first, after: $after) { pageInfo { hasNextPage endCursor } edges { cursor node { title id } } } } `; const fetcher = (query, {first, after}) => request('/api/graphql', query, { first, after }) const AwesomeLinks = () => { const { data, error, mutate } = useSWR( [AllLinksQuery, {first:10, after:null}] , fetcher , { revalidateIfStale:true } ); /** rest of the component ***/ }
在Apollo Client中可通过fetchMore实现类似需求,但对SWR中useSWRInfinite的适配方式不明确。
解决方案
1. 保留原有Fetcher逻辑
原fetcher无需大幅修改,它依然接收GraphQL查询语句和分页变量参数,后续useSWRInfinite会通过请求key传递这些参数。
2. 核心改造:useSWRInfinite实现
关键是实现getKey函数,该函数负责为每一页生成唯一请求标识,同时根据上一页的返回数据自动计算当前页的游标参数:
const AwesomeLinks = () => { // 定义每页加载的数据量 const PAGE_SIZE = 10; // 生成每页请求的Key,控制分页参数 const getKey = (pageIndex, previousPageData) => { // 第一页:无历史数据,after设为null if (pageIndex === 0) return [AllLinksQuery, { first: PAGE_SIZE, after: null }]; // 上一页已无更多数据,返回null终止后续请求 if (!previousPageData?.links?.pageInfo?.hasNextPage) return null; // 后续页:用上一页的endCursor作为当前页的after参数 return [ AllLinksQuery, { first: PAGE_SIZE, after: previousPageData.links.pageInfo.endCursor } ]; }; // 初始化useSWRInfinite const { data, error, size, setSize, isLoading, isLoadingMore } = useSWRInfinite( getKey, fetcher, { revalidateIfStale: true } ); // 合并所有页面的edges数据,统一渲染 const allEdges = data ? data.flatMap(page => page.links.edges) : []; // 判断是否还有更多数据可加载 const hasMore = data?.[data.length - 1]?.links?.pageInfo?.hasNextPage ?? false; // 触发加载下一页的函数 const loadMore = () => { setSize(size + 1); }; // 错误状态处理 if (error) return <div>加载失败</div>; // 初始加载状态处理 if (!data && isLoading) return <div>加载中...</div>; return ( <div> {/* 渲染所有获取到的链接 */} {allEdges.map((edge) => ( <div key={edge.node.id}>{edge.node.title}</div> ))} {/* 加载更多按钮 */} {hasMore && ( <button onClick={loadMore} disabled={isLoadingMore}> {isLoadingMore ? '加载中...' : '加载更多'} </button> )} </div> ); };
关键逻辑说明
- getKey函数:
- 第一页直接传递
after: null获取初始数据 - 后续页面自动复用上一页的
endCursor作为当前页的游标参数 - 当检测到上一页无更多数据时,返回
null终止后续请求
- 第一页直接传递
- 数据合并:通过
flatMap将多页的edges数组合并为单个数组,简化渲染逻辑 - 加载状态控制:利用
isLoadingMore控制按钮状态,避免重复触发请求
该方案完全适配原有的GraphQL游标分页逻辑,同时保留了SWR的缓存、重验证特性,实现了和Apollo Client fetchMore一致的无限滚动效果。
内容的提问来源于stack exchange,提问作者David Nelband
相关产品推荐
相关产品推荐

