urql缓存更新时出现重复数据问题——urql、GraphQL、React
核心问题根源
你遇到的重复问题本质是缓存更新时未做去重处理,加上urql GraphCache在处理参数初始化延迟(默认参数从localStorage加载)的查询时,不同参数的查询实例可能意外共享缓存引用,导致新记录被重复追加。另外,直接修改缓存返回的data对象(引用类型)会进一步加剧数据共享问题。
具体修复步骤
1. 避免直接修改缓存数据,强制创建副本+去重
urql的缓存数据是引用类型,直接修改data.readMeanings.meanings会影响其他关联查询。必须创建数组副本,并基于记录唯一标识(如id)去重:
createMeanings: (result, args, cache, info) => { const newMeanings = (result as CreateMeaningsMutation) .createMeanings.meanings; if (!newMeanings) return; cache .inspectFields("Query") .filter((field) => field.fieldName === "readMeanings") .forEach((field) => { cache.updateQuery( { query: ReadMeaningsDocument, variables: field.arguments as QueryReadMeaningsArgs, }, (data: ReadMeaningsQuery | null) => { if (!data?.readMeanings?.meanings) return data; // 创建数据副本,避免修改原缓存引用 const combinedMeanings = [...newMeanings, ...data.readMeanings.meanings]; // 基于唯一id去重,保留最新的前置记录 const uniqueMeanings = Array.from( new Map(combinedMeanings.map(item => [item.id, item])).values() ); return { ...data, readMeanings: { ...data.readMeanings, meanings: uniqueMeanings, }, }; } ); }); },
2. 过滤目标查询,避免更新无关实例
你提到存在默认参数和实际加载参数的两种readMeanings查询,可在filter阶段增加参数校验,只更新符合业务需求的查询(比如仅更新按createdAt DESC排序的查询,确保新记录显示在顶部):
.filter((field) => field.fieldName === "readMeanings" && field.arguments?.options?.sorting?.by === "createdAt" && field.arguments?.options?.sorting?.order === "DESC" )
3. 配置keyArgs明确缓存区分规则
在GraphCache配置中为readMeanings指定keyArgs,确保不同参数的查询被独立缓存,避免数据共享:
const cache = createGraphCache({ resolvers: { Query: { readMeanings: { keyArgs: ["options"], // 将options作为缓存键的核心标识 }, }, }, // 其他配置项 });
额外建议
- 检查组件中
useQuery的调用逻辑,避免重复创建相同参数的查询实例 - 若手动缓存更新复杂度高,可在mutation完成后通过
refetchQueries主动刷新目标查询(但手动更新性能更优)
原提问内容
更新说明:
我对新记录的缓存机制进行了调查,发现以下现象:
- 首次插入(
new1)时,会在当前(首次使用的)查询中添加一条额外记录(默认查询:[new1,...],首次使用查询:[new1,new1,...])。 - 第二次插入时,会将上一次插入的记录(
new1)添加到默认查询中,同时仍会在首次使用的查询中添加一条额外记录(默认查询:[new2,new1,new1,...],首次使用查询:[new2,new2,new1,new1,...],第二次查询:[new2,new1,...]——此查询结果正确)。 - 后续每次插入都会重复上述第二步的情况,但第二次及之后使用的查询不受影响(我已检查最多四个查询的情况)。
我有createMeanings mutation和readMeanings query。当创建Meaning类型的新记录后,希望它显示在表格顶部。
readMeanings的参数如下:{options: {limit: 5, sorting: {by: "createdAt", order: "DESC"}}}。因此我存在多个readMeanings查询实例。
问题在于,新创建的记录经常(但并非总是)在缓存中出现重复,甚至重复多次。
以下是我参考官方文档编写的缓存更新代码:
createMeanings: (result, args, cache, info) => { const newMeanings = (result as CreateMeaningsMutation) .createMeanings.meanings; if (newMeanings) { cache .inspectFields("Query") .filter((field) => field.fieldName === "readMeanings") .forEach((field) => { cache.updateQuery( { query: ReadMeaningsDocument, variables: field.arguments as QueryReadMeaningsArgs, }, (data: ReadMeaningsQuery | null) => { if (data?.readMeanings.meanings) { console.log("field.arguments:", field.arguments); console.log("newMeanings", newMeanings); data.readMeanings.meanings = [ ...newMeanings, ...data.readMeanings.meanings, ]; // Or: data.readMeanings.unshift(...newMeanings); } console.log("data", data); return data; } ); }); } },
查看日志时,我看到如下内容:[Log] field.arguments: – {options: {limit: 10, sorting: {by: "updatedAt", order: "DESC"}}}*data.readMeanings.meanings has 6 records
[Log] field.arguments: – {options: {limit: 5, sorting: {by: "createdAt", order: "DESC"}}}data.readMeanings.meanings has 7 records
在这7条记录中,顶部的两条是新创建记录的重复项。
看起来data似乎在这两个查询之间被共享了。
是否有什么我忽略的明显问题?希望能得到相关建议!
*不确定是否相关,但这些是默认参数,我通过useEffect从localStorage加载实际参数。
内容的提问来源于stack exchange,提问作者vls9

