You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Apollo Client中mutation的update回调第二次为何传入未修改缓存?

问题原因分析

这是Apollo Client乐观响应机制的正常设计行为:

  1. 当触发带乐观响应的mutation时,Apollo会先执行乐观更新,调用一次update回调(此时options.data是你定义的乐观响应数据),同时将这次修改记录到缓存的乐观层。
  2. 当后端真实响应返回后,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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 06:05:12