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

Apollo GraphQL Client:如何在watchQuery中区分乐观响应与真实响应

解决Apollo Client中watchQuery区分乐观响应与真实响应的问题

我刚好遇到过类似的困扰,不需要给GraphQL数据加额外标记字段,这里有几个更优雅的解决方案:

方法一:利用Apollo Cache存储乐观更新状态标记

这种方法适合需要跨组件共享乐观状态的场景,通过Apollo的本地缓存来维护一个全局的标记:

  1. 定义一个本地GraphQL片段,用来存储乐观状态:
fragment MyMutationOptimisticStatus on Query {
  isMyMutationOptimistic: Boolean!
}
  1. 在mutation的update函数中,根据optimistic参数写入状态:
const [myMutation] = useMutation(MY_MUTATION, {
  optimisticResponse: () => ({
    // 你的乐观响应数据结构
    myMutation: { /* ... */ }
  }),
  update(cache, _, { optimistic }) {
    // 先执行你的myQuery缓存更新逻辑
    const existingData = cache.readQuery({ query: MY_QUERY });
    const updatedData = // 根据乐观/真实数据更新后的结构
    cache.writeQuery({ query: MY_QUERY, data: updatedData });

    // 写入乐观状态标记到ROOT_QUERY
    cache.writeFragment({
      id: 'ROOT_QUERY',
      fragment: gql`${MyMutationOptimisticStatus}`,
      data: { isMyMutationOptimistic: optimistic }
    });
  }
});
  1. 在watchQuery订阅中读取这个标记:
// 如果用useQuery组件
const { data: queryData } = useQuery(MY_QUERY);
const { data: optimisticStatus } = useQuery(gql`
  query GetOptimisticStatus {
    isMyMutationOptimistic @client
  }
`);

// 渲染时判断
return (
  <div className={optimisticStatus.isMyMutationOptimistic ? "pending-optimistic" : ""}>
    {queryData.content}
    <button disabled={optimisticStatus.isMyMutationOptimistic}>关键操作</button>
  </div>
);

// 如果用原生watchQuery
const observableQuery = client.watchQuery({ query: MY_QUERY });
observableQuery.subscribe({
  next(result) {
    const status = client.readFragment({
      id: 'ROOT_QUERY',
      fragment: gql`${MyMutationOptimisticStatus}`
    });
    const isOptimistic = status.isMyMutationOptimistic;
    // 根据状态处理UI逻辑
  }
});

方法二:组件内本地状态跟踪(简单直接)

如果乐观状态只在当前组件内使用,用本地状态会更简单:

  1. 组件内维护一个状态标记,在mutation生命周期中更新:
const [isOptimisticActive, setIsOptimisticActive] = useState(false);

const [myMutation] = useMutation(MY_MUTATION, {
  optimisticResponse: () => ({ /* ... */ }),
  update(_, _, { optimistic }) {
    // 执行缓存更新逻辑
    // 设置乐观状态
    setIsOptimisticActive(optimistic);
  },
  onCompleted: () => {
    // 确保真实响应回来后重置状态(防止异常情况)
    setIsOptimisticActive(false);
  },
  onError: () => {
    // 出错时也重置状态
    setIsOptimisticActive(false);
  }
});

// 调用mutation前也可以主动设置状态(可选,update里的optimistic参数已经能覆盖)
const triggerMutation = () => {
  setIsOptimisticActive(true);
  myMutation();
};
  1. 直接在UI中使用这个状态:
const { data } = useQuery(MY_QUERY);

return (
  <div style={{ opacity: isOptimisticActive ? 0.7 : 1 }}>
    {data?.items.map(item => (
      <div key={item.id}>
        {item.name}
        <button disabled={isOptimisticActive}>编辑</button>
      </div>
    ))}
  </div>
);

方法三:利用乐观实体的临时ID(仅适用于新增实体场景)

如果你的mutation是新增实体,乐观更新的实体ID会包含__optimistic前缀,你可以通过cache.identify来判断:

observableQuery.subscribe({
  next(result) {
    const newItem = result.data.newItem;
    const itemId = client.cache.identify(newItem);
    const isOptimistic = itemId.includes('__optimistic');
    // 根据这个标记处理UI
  }
});

这个方法局限性较大,只适合新增实体的情况,更新现有实体时不适用。


内容的提问来源于stack exchange,提问作者Dolios

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 03:57:46