Apollo Client中mutation的update回调第二次为何传入未修改缓存?
问题原因分析
这是Apollo Client乐观响应机制的正常设计行为:
- 当触发带乐观响应的mutation时,Apollo会先执行乐观更新,调用一次
update回调(此时options.data是你定义的乐观响应数据),同时将这次修改记录到缓存的乐观层。 - 当后端真实响应返回后,Apollo会自动回滚所有乐观更新(把缓存恢复到乐观修改前的原始状态),然后再调用一次
update回调(此时options.data是后端返回的真实数据)。
你看到第二次update调用时缓存回到未修改状态,就是因为这个回滚操作。而你在两次update中重复执行了相同的cache.modify逻辑,相当于在回滚后又做了一次删除操作,这就导致多次快速触发mutation时,UI会短暂显示原始数据再被修改,出现闪烁问题。
另外,cache.modify的optimistic: true参数只有在乐观阶段调用时才生效(修改仅作用于乐观层);真实响应阶段调用时该参数会被忽略,修改直接作用于主缓存层。你觉得它没效果,是因为第二次调用时缓存已经被回滚,看起来修改结果和第一次一致,但实际上两次操作的是不同的缓存层。
解决方案
1. 区分乐观/真实响应阶段,执行对应逻辑
通过options.context.optimistic判断当前是否处于乐观阶段,分别处理缓存更新:
const { data } = await removeReactionMutation({ variables, optimisticResponse, update: ((cache, options) => { const isOptimistic = options.context?.optimistic; const messageId = GUID2ACID(variables.messageId); if (isOptimistic) { // 乐观阶段:仅修改乐观层 cache.modify({ id: messageId, fields: { reactions(existing) { return existing.filter( (reaction: IAPIMessageReaction) => reaction.userId !== identity.guid || reaction.emojiId !== variables.emojiId ); }, }, optimistic: true, }); } else { // 真实响应阶段:用后端返回的数据更新主缓存 const updatedReactions = options.data?.removeReaction?.message?.reactions; if (updatedReactions) { cache.modify({ id: messageId, fields: { reactions() { return updatedReactions; }, }, }); } } }), });
2. 让Apollo自动处理缓存更新(推荐)
如果你的removeReaction mutation返回完整的更新后Message对象(包含最新的reactions),且乐观响应的结构和后端返回完全一致,Apollo可以自动处理缓存更新,无需手动写update回调。
示例乐观响应结构:
const optimisticResponse = { __typename: 'Mutation', removeReaction: { __typename: 'Message', id: variables.messageId, reactions: existingReactions.filter( (r: IAPIMessageReaction) => r.userId !== identity.guid || r.emojiId !== variables.emojiId ), // 其他Message字段需与后端返回保持一致 }, };
这种方式下,Apollo会自动将乐观响应的数据更新到缓存,后端响应返回后再自动用真实数据替换乐观数据,全程无需手动干预。
3. 使用cache.writeFragment手动更新
如果必须手动操作缓存,cache.writeFragment可以更直观地控制缓存写入:
const { data } = await removeReactionMutation({ variables, optimisticResponse, update: ((cache, options) => { const messageId = GUID2ACID(variables.messageId); // 读取当前缓存中的Message数据 const message = cache.readFragment({ id: messageId, fragment: gql` fragment MessageReactions on Message { reactions { userId emojiId } } `, }); if (!message) return; const updatedReactions = message.reactions.filter( (reaction: IAPIMessageReaction) => reaction.userId !== identity.guid || reaction.emojiId !== variables.emojiId ); // 写入更新后的缓存,自动区分乐观/真实层 cache.writeFragment({ id: messageId, fragment: gql` fragment UpdateMessageReactions on Message { reactions { userId emojiId } } `, data: { ...message, reactions: updatedReactions }, optimistic: options.context?.optimistic, }); }), });
内容的提问来源于stack exchange,提问作者Georgy Nemtsov
相关产品推荐
相关产品推荐

