Apollo分页查询缓存已存在仍请求服务器问题排查
解决Apollo Client分页回退重复请求服务端的问题
问题根源分析
你遇到的重复请求问题,主要来自三个方面:
- 查询变量不完整,缓存键未包含
filter、debouncedSearch等影响结果的参数,导致缓存逻辑混乱 - 总数字段
privateCloudProjectsCount未配置缓存策略,每次查询都会触发服务端请求 - 分页逻辑未判断目标页面数据是否已缓存,可能误触发
fetchMore
具体解决方案
1. 补全查询变量
将filter和debouncedSearch加入useQuery和fetchMore的变量中,确保Apollo Client能根据完整参数生成唯一缓存键:
export default function Projects() { const { debouncedSearch } = useContext(SearchContext); const { filter } = useContext(FilterContext); const [currentPage, setCurrentPage] = useState(0); const pageSize = 10; const { loading, data, fetchMore, error } = useQuery(ALL_PROJECTS, { fetchPolicy: "cache-first", nextFetchPolicy: "cache-first", variables: { offset: 0, limit: pageSize, filter: filter, search: debouncedSearch, }, }); const getNextPage = useCallback( (page) => { setCurrentPage(page); const targetOffset = page * pageSize; // 仅当目标页面数据未加载时,才调用fetchMore const existingDataLength = data?.privateCloudProjectsPaginated?.length || 0; if (targetOffset >= existingDataLength) { fetchMore({ variables: { offset: targetOffset, limit: pageSize, filter: filter, search: debouncedSearch, }, }); } }, [filter, debouncedSearch, fetchMore, data, pageSize] ); // ... 其余代码不变 }
2. 配置总数字段的缓存策略
修改InMemoryCache的typePolicies,给privateCloudProjectsCount添加缓存规则,基于filter和search缓存总条数:
const cache = new InMemoryCache({ typePolicies: { Query: { fields: { privateCloudProjectsPaginated: offsetLimitPagination({ // 指定除offset/limit外的缓存键参数 keyArgs: ['filter', 'search'], }), privateCloudProjectsCount: { // 总条数仅依赖filter和search,与分页参数无关 keyArgs: ['filter', 'search'], merge(existing, incoming) { // 直接覆盖旧值,确保总条数始终最新 return incoming; }, }, }, }, }, });
3. 优化表格数据切片逻辑
确保表格仅从缓存的全量数据中切片当前页内容,避免触发不必要的查询:
// 在组件中计算当前页的rows const currentRows = useMemo(() => { if (!data?.privateCloudProjectsPaginated) return []; const start = currentPage * pageSize; const end = start + pageSize; return data.privateCloudProjectsPaginated.slice(start, end).map(projectsToRows); }, [data, currentPage, pageSize]); // 渲染表格时使用currentRows return ( <StickyTable onClickPath="/private-cloud/admin/project/" onNextPage={getNextPage} columns={columns} rows={loading ? [] : currentRows} count={loading ? 0 : data.privateCloudProjectsCount} title="Projects" loading={loading} /> );
验证效果
完成以上修改后:
- 首次请求第一页时,从服务端获取数据并缓存
- 切换到未加载过的页面时,调用
fetchMore获取新数据并合并到缓存 - 回退到已加载过的页面时,直接从缓存切片显示数据,不会发起服务端请求
内容的提问来源于stack exchange,提问作者Oamar Kanji
相关产品推荐
相关产品推荐

