如何用Apollo Client实现多列表GraphQL fetchMore分页,避免重复请求
多列表独立分页实现方案(Apollo Client + Server)
针对你页面中三个横向滚动列表的分页需求,以下是具体实现步骤,满足初始一次请求、单列表分页不影响其他列表的要求:
1. 初始数据加载
直接使用你提供的查询语句,页面初始化时传入所有offset为0的变量,一次请求拉取三个列表的第一页数据:
const { data, fetchMore, loading } = useQuery(QUERY, { variables: { favoritesOffset: 0, featuredOffset: 0, recentOffset: 0, }, });
Apollo Client会自动将这次请求的结果缓存起来,后续操作基于此缓存进行更新。
2. 单列表下一页加载逻辑
当用户滚动到某个列表底部时,只更新对应列表的offset参数,通过fetchMore方法拉取下一页数据,并合并到现有缓存中,不修改其他列表的数据。
以加载「favorites」列表下一页为例:
const loadMoreFavorites = () => { // 计算下一页的offset:当前已加载的条目数(因为每页limit是5) const currentLoadedCount = data?.favorites?.length || 0; const nextOffset = currentLoadedCount; fetchMore({ // 仅更新favorites的offset变量,其他变量保持原有值 variables: { favoritesOffset: nextOffset }, // 合并新旧数据,只更新favorites字段 updateQuery: (prevData, { fetchMoreResult }) => { if (!fetchMoreResult) return prevData; return { ...prevData, favorites: [...prevData.favorites, ...fetchMoreResult.favorites], // featured和recent字段直接沿用之前的缓存数据,不做修改 }; }, }); };
同理,「featured」和「recent」列表的加载逻辑只需替换对应的offset变量即可:
// 加载featured下一页 const loadMoreFeatured = () => { const currentLoadedCount = data?.featured?.length || 0; fetchMore({ variables: { featuredOffset: currentLoadedCount }, updateQuery: (prevData, { fetchMoreResult }) => { if (!fetchMoreResult) return prevData; return { ...prevData, featured: [...prevData.featured, ...fetchMoreResult.featured], }; }, }); }; // 加载recent下一页(注意recent的limit是10,offset计算逻辑一致) const loadMoreRecent = () => { const currentLoadedCount = data?.recent?.length || 0; fetchMore({ variables: { recentOffset: currentLoadedCount }, updateQuery: (prevData, { fetchMoreResult }) => { if (!fetchMoreResult) return prevData; return { ...prevData, recent: [...prevData.recent, ...fetchMoreResult.recent], }; }, }); };
3. 服务端适配建议
为了让前端能判断是否还有更多数据可加载,建议在listResponse片段中增加totalCount字段(需要服务端resolver支持返回总条目数):
fragment listResponse on EventList { items { id title # 你的其他业务字段 } totalCount # 该类型下的总条目数 }
对应的查询语句也要调整为返回包含items和totalCount的结构:
query Sections( $favoritesOffset: Int = 0 $featuredOffset: Int = 0 $recentOffset: Int = 0 ) { favorites: events(limit: 5, offset: $favoritesOffset, search: "FAVORITES") { items { ...listResponseFields } totalCount } # ... 其他列表同理 } fragment listResponseFields on Event { id title # 业务字段 }
前端就可以通过data.favorites.totalCount > data.favorites.items.length来判断是否还有下一页,避免无效请求。
4. 缓存注意事项
- Apollo Client会自动缓存不同参数的查询,但
updateQuery会将新数据合并到初始的缓存条目里,确保组件始终能拿到该列表的全部已加载数据。 - 如果需要更精细的缓存控制,可以使用
cache.modify手动更新缓存,但fetchMore的updateQuery已经足够满足当前需求。
内容的提问来源于stack exchange,提问作者Jeremy Gillick
相关产品推荐
相关产品推荐

