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

React中Apollo连续调用被取消问题及Client实例化优化咨询

问题

在React代码中编写了如下Hook及点击事件函数:

const [getDataFromGraphql] = useGetDataFromGraphql();

const getData = async (...) => {
  const res = await getDataFromGraphql({ variables: { ... } });
  ...
}

其中useGetDataFromGraphql是封装useLazyQuery的自定义Hook:

export default function useGetDataFromGraphql() {
  return useLazyQuery<..,..>(
    query,
    {
      client: aPrivateClient,
    }
  );
}

aPrivateClient是一个ApolloClient实例:

export default new ApolloClient({
  link: from([authLink, authExpiryLink, httpLink]),
  cache,
  defaultOptions,
});

遇到的异常:短时间内多次点击不同按钮(传入不同变量调用同一个getData函数)时,除最后一次调用外,其余所有调用都会被取消,最后一次的结果会覆盖之前的所有结果,尽管每次点击传入的变量不同。

曾尝试对Apollo Client进行memoize但未解决问题,且应用连接了多个客户端,需在Hook中指定具体客户端,单客户端的文档示例不适用。

需求:如何重构配置避免连续网络调用被取消?若客户端实例化是根因,如何确保ApolloClient不会被重新实例化?


解决方案

1. 为每个请求添加唯一上下文标识,避免请求被自动取消

Apollo的useLazyQuery默认会取消同一查询文档+同一客户端实例下的未完成请求,即使变量不同。可以通过为每个请求添加唯一的context标识符,让Apollo将它们识别为独立请求:

const getData = async (...) => {
  const res = await getDataFromGraphql({ 
    variables: { ... },
    // 生成唯一请求ID,确保每个请求上下文独立
    context: { requestId: crypto.randomUUID() }
  });
  ...
}

也可结合业务需求修改fetchPolicy(比如network-only),进一步明确请求的独立性:

const getData = async (...) => {
  const res = await getDataFromGraphql({ 
    variables: { ... },
    fetchPolicy: 'network-only',
    context: { requestId: crypto.randomUUID() }
  });
  ...
}

2. 确保ApolloClient实例为单例,避免重复创建

尽管当前代码导出了单例aPrivateClient,仍需检查所有导入该客户端的位置,确保没有在组件、Hook内部重复执行new ApolloClient()创建新实例。

若需根据不同场景动态创建客户端,可通过memoize工具缓存实例:

import memoize from 'lodash.memoize';

// 缓存客户端实例,确保同一配置只创建一次
const createPrivateClient = memoize(() => {
  return new ApolloClient({
    link: from([authLink, authExpiryLink, httpLink]),
    cache,
    defaultOptions,
  });
});

// 使用时直接调用,始终返回单例
const aPrivateClient = createPrivateClient();

3. 改造自定义Hook,自动处理请求独立性

修改useGetDataFromGraphql,封装执行逻辑时自动为每个请求添加唯一标识,避免调用方重复编写代码:

export default function useGetDataFromGraphql() {
  const [executeQuery] = useLazyQuery<..,..>(
    query,
    {
      client: aPrivateClient,
    }
  );

  // 封装执行函数,自动注入唯一请求ID
  const executeWithUniqueContext = async (options) => {
    return executeQuery({
      ...options,
      context: {
        ...options.context,
        requestId: crypto.randomUUID()
      }
    });
  };

  return [executeWithUniqueContext];
}

4. 排查自定义Link的拦截逻辑

检查authLink、authExpiryLink等自定义Link,确认是否存在主动取消所有未完成请求的逻辑(比如token刷新时批量取消请求)。若有,需修改逻辑,仅取消与当前操作相关的请求,而非全部请求。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 13:16:26