React Apollo Client v3偏移量分页:缓存未命中重复请求排查
Apollo Client 偏移量分页缓存不命中问题
我按照Apollo Client文档实现了基于偏移量的分页功能:
- 从第1页切到第2页、第2页切到第3页时,正常向GraphQL服务器发起查询
- 但从第3页返回第2页、第2页返回第1页时,依然触发查询请求,说明没读取缓存(缓存已按预期存为单个列表)
请问问题出在哪?有没有人完全按文档实现过分页功能?
组件中useQuery调用
const {loading:loadingHistory, data:historyData, fetchMore} = useQuery(getHistoryOffsetPaged, { variables: { offset: 0, limit: store.selectedNumberOfElements } })
分页按钮逻辑
<PageButtons totalPages={store.historyTotalPages} currentPage={store.historyCurrentPage} changePage={page => { fetchMore({ variables: { offset: (page - 1) * store.selectedNumberOfElements, limit: store.selectedNumberOfElements } }) store.setHistoryCurrentPage(page) }} />
GraphQL查询定义
export const getHistoryOffsetPaged = gql` query( $offset: Int, $limit: Int) { getHistoryOffsetPaged( offset: $offset, limit: $limit) { id dateTime curFrom curTo exRate sumBeforeConversion sumAfterConversion averageExRate } } `
缓存配置
cache: new InMemoryCache({ typePolicies: { Query: { fields: { getHistoryOffsetPaged: { keyArgs: false, merge(existing, incoming, { args: { offset = 0 } }) { const merged = existing ? existing.slice(0) : []; for (let i = 0; i < incoming.length; ++i) { merged[offset + i] = incoming[i]; } return merged; }, read(existing, { args }) { return existing && existing.slice( args?.offset, args?.offset + args?.limit); } } } } } })
问题原因分析
核心问题是用错了分页触发方式:
- fetchMore的设计目的是追加加载数据(比如滚动加载),不是切换页面。它不会更新useQuery的核心查询变量,只是把新数据合并到缓存列表中
- 你初始的useQuery始终用
offset:0的变量,切换页面时调用fetchMore传入新offset,但Apollo会为每个不同的变量组合生成独立的缓存查询条目。返回旧页面时,相当于用新的变量组合发起查询,而该组合的缓存条目不存在,因此触发网络请求
修正方案
直接通过更新useQuery的变量来切换页面,让Apollo自动利用缓存:
- 调整useQuery的变量,关联当前页码和每页条数:
const {loading:loadingHistory, data:historyData} = useQuery(getHistoryOffsetPaged, { variables: { offset: (store.historyCurrentPage - 1) * store.selectedNumberOfElements, limit: store.selectedNumberOfElements }, // 可选:根据需求设置fetchPolicy,默认是cache-first fetchPolicy: 'cache-and-network' })
- 简化分页按钮逻辑,只需更新当前页码:
<PageButtons totalPages={store.historyTotalPages} currentPage={store.historyCurrentPage} changePage={page => { store.setHistoryCurrentPage(page) }} />
- 保持现有typePolicies配置不变,它会继续负责合并不同offset的查询结果到统一缓存列表,read函数会根据当前变量返回对应的数据切片。
补充说明
当你切换页面时,useQuery会自动根据新的offset/limit变量,先调用read函数从合并后的缓存列表中提取对应切片,如果数据存在就直接返回缓存,不会发起网络请求。只有当缓存中没有对应数据时,才会触发查询。
内容的提问来源于stack exchange,提问作者Alex
相关产品推荐
相关产品推荐

