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

Apollo Client Relay风格分页:求分页读取函数示例

自定义relayStylePagination分页读取函数示例

默认relayStylePagination生成的read函数会忽略查询参数,直接返回缓存中所有合并后的分页数据。如果需要让它根据传入的分页参数(如first、after)返回单页数据,可以按以下方式自定义:

1. 覆盖read函数的自定义字段策略

import { relayStylePagination } from '@apollo/client/utilities';
import { gql, InMemoryCache } from '@apollo/client';

// 基于默认relay策略扩展
const customRelayPagination = relayStylePagination();

// 重写read方法
customRelayPagination.read = function (existing, { args, cache }) {
  // 先调用默认read获取所有缓存的分页数据
  const fullDataset = relayStylePagination().read(existing, { args, cache });
  
  // 无分页参数时返回全部数据,兼容原有逻辑
  if (!args?.first) return fullDataset;

  let startIdx = 0;
  // 处理after游标,定位起始位置
  if (args.after) {
    // 读取游标对应的节点(需根据你的游标格式调整缓存ID和Fragment)
    const targetNode = cache.readFragment({
      id: `YourNodeType:${args.after}`, // 示例:假设游标是节点ID,缓存ID格式为"类型:ID"
      fragment: gql`
        fragment NodeIdFragment on YourNodeType {
          id
        }
      `,
    });

    if (targetNode) {
      // 找到该节点在edges中的位置,下一个位置就是分页起始点
      startIdx = fullDataset.edges.findIndex(edge => edge.node.id === targetNode.id) + 1;
    }
  }

  // 截取对应数量的条目
  const paginatedEdges = fullDataset.edges.slice(startIdx, startIdx + args.first);

  // 返回符合分页要求的结构,更新pageInfo状态
  return {
    ...fullDataset,
    edges: paginatedEdges,
    pageInfo: {
      ...fullDataset.pageInfo,
      hasNextPage: paginatedEdges.length === args.first,
      hasPreviousPage: startIdx > 0,
    },
  };
};

// 在缓存配置中使用自定义策略
const cache = new InMemoryCache({
  typePolicies: {
    Query: {
      fields: {
        // 替换成你的分页字段名,比如"posts"、"comments"
        yourPaginatedField: customRelayPagination,
      },
    },
  },
});

2. fetchMore后更新查询变量

自定义read函数依赖查询参数返回对应页面,因此调用fetchMore后必须更新原始查询的变量,确保组件重新渲染时read函数能拿到正确参数:

import { useQuery } from '@apollo/client';

const YOUR_PAGINATED_QUERY = gql`
  query GetItems($first: Int!, $after: String) {
    yourPaginatedField(first: $first, after: $after) {
      edges {
        node { id /* 其他字段 */ }
      }
      pageInfo {
        endCursor
        hasNextPage
      }
    }
  }
`;

function YourComponent() {
  const { data, fetchMore, variables, setVariables } = useQuery(YOUR_PAGINATED_QUERY, {
    variables: { first: 10, after: null },
  });

  const handleLoadNextPage = async () => {
    if (!data?.yourPaginatedField?.pageInfo?.hasNextPage) return;

    await fetchMore({
      variables: {
        after: data.yourPaginatedField.pageInfo.endCursor,
      },
    });

    // 更新原始查询变量,让read函数下次用新参数返回对应页
    setVariables({
      ...variables,
      after: data.yourPaginatedField.pageInfo.endCursor,
    });
  };

  return (
    // 组件渲染逻辑
  );
}

关键注意点

  • 游标的缓存ID格式需要和你的实际情况匹配:如果后端返回的游标不是节点ID,需要调整cache.readFragment中的id参数,比如有些后端会返回编码后的游标,这时可能需要先解码再匹配节点。
  • pageInfo的hasNextPage和hasPreviousPage需要根据截取后的结果重新计算,保证前端分页控件状态正确。

内容的提问来源于stack exchange,提问作者Treznorx

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 18:32:48