You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何结合带变量的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 00:56:02