Apollo Client:使用offsetLimitPagination()执行删除操作后出现重复项问题
解决InMemoryCache结合offsetLimitPagination后删除mutation导致列表重复的问题
嘿,这个问题我之前在项目里也踩过坑!让我给你拆解一下原因和解决办法:
问题根源
你用的offsetLimitPagination()是Apollo提供的分页缓存策略,它的默认行为是缓存不同分页参数(offset/limit)对应的列表结果,并且默认的merge逻辑是把新查询的结果追加到已有缓存的列表后面。当你执行删除mutation后,如果没有主动告知缓存要移除对应的条目,Apollo不会自动从已缓存的分页列表中删除这个ID的项——旧的分页缓存还保留着已删除的条目,而后续查询可能因为偏移量变化又拉取了新的结果,最终就出现了ID重复的“幽灵条目”。
具体解决办法
1. 在mutation中手动更新缓存(最推荐)
通过update回调函数直接操作缓存,过滤掉已删除的条目,这是最精准高效的方式:
mutateData({ variables: { input : { id, delete: true, } }, update(cache, { data }) { // 替换成你用来获取datas列表的查询语句 const DatasQuery = gql` query GetDatas($offset: Int, $limit: Int) { datas(offset: $offset, limit: $limit) { id # 其他字段... } } `; // 读取当前缓存中对应的分页数据 const existingCache = cache.readQuery({ query: DatasQuery, variables: { offset: 0, limit: 10 } // 这里填你实际使用的分页变量 }); if (existingCache?.datas) { // 过滤掉已删除ID的条目 const updatedDatas = existingCache.datas.filter(item => item.id !== id); // 将更新后的列表写回缓存 cache.writeQuery({ query: DatasQuery, variables: { offset: 0, limit: 10 }, data: { datas: updatedDatas } }); } } });
2. 优化TypePolicies配置
调整offsetLimitPagination()的merge逻辑,同时给数据类型指定唯一键,让Apollo能自动识别重复条目:
const cache = new InMemoryCache({ typePolicies: { Query: { fields: { datas: { ...offsetLimitPagination(), // 自定义merge逻辑,合并时自动去重 merge(existing, incoming) { if (!existing) return incoming; const existingIds = new Set(existing.map(item => item.id)); return [...existing, ...incoming.filter(item => !existingIds.has(item.id))]; }, // 如果你的分页查询只有offset/limit,不需要按变量拆分缓存的话可以设为false keyArgs: false } } }, // 给你的数据条目类型指定唯一键(替换成实际的类型名) DataItem: { keyFields: ['id'] } } });
3. 强制重新查询数据(简单但低效)
如果不想手动操作缓存,可以用refetchQueries让Apollo自动重新执行列表查询,获取最新数据:
mutateData({ variables: { input : { id, delete: true, } }, refetchQueries: [{ query: YOUR_DATAS_QUERY, // 替换成你的列表查询语句 variables: { offset: 0, limit: 10 } // 实际分页变量 }] });
总结
最推荐第一种手动更新缓存的方式,既能避免额外的网络请求,又能精准控制缓存的更新逻辑。如果项目中有大量类似的分页场景,第二种优化TypePolicies的方式能一劳永逸解决重复问题。
内容的提问来源于stack exchange,提问作者Galih laras prakoso
相关产品推荐
相关产品推荐

