Apollo GraphQL Client:如何在watchQuery中区分乐观响应与真实响应
解决Apollo Client中watchQuery区分乐观响应与真实响应的问题
我刚好遇到过类似的困扰,不需要给GraphQL数据加额外标记字段,这里有几个更优雅的解决方案:
方法一:利用Apollo Cache存储乐观更新状态标记
这种方法适合需要跨组件共享乐观状态的场景,通过Apollo的本地缓存来维护一个全局的标记:
- 定义一个本地GraphQL片段,用来存储乐观状态:
fragment MyMutationOptimisticStatus on Query { isMyMutationOptimistic: Boolean! }
- 在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 } }); } });
- 在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逻辑 } });
方法二:组件内本地状态跟踪(简单直接)
如果乐观状态只在当前组件内使用,用本地状态会更简单:
- 组件内维护一个状态标记,在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(); };
- 直接在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
相关产品推荐
相关产品推荐

