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

Electron中使用Apollo Client出现useQuery重复请求问题

解决Electron中Apollo Client多重复请求、缓存失效问题

核心问题分析

  1. Apollo Client实例重复创建:你的useClient钩子每次调用都会生成全新的ApolloClient和InMemoryCache实例,一旦父组件重渲染,就会创建新客户端,之前的缓存完全失效,导致所有查询重复发起。
  2. 相同__typename的缓存冲突:两个查询返回的父对象__typename相同,但无唯一标识字段,Apollo无法区分两个缓存条目,导致缓存覆盖或无法正确存储,进而触发重复请求。

解决方案

1. 缓存Apollo Client实例,避免重复创建

修改useClient,用useMemo缓存客户端实例,仅当authToken变化时重新创建:

import { useMemo } from 'react';
import { ApolloClient, createHttpLink, InMemoryCache, setContext } from '@apollo/client';

export const useClient = () => {
  const authToken = sessionStorage.getItem('token');

  const client = useMemo(() => {
    const httpLink = createHttpLink({
      uri: 'https://api',
    });

    const authLink = setContext((_, { headers }) => ({
      headers: {
        ...headers,
        authorization: authToken ? `Bearer ${authToken}` : '',
      },
    }));

    return new ApolloClient({
      link: authLink.concat(httpLink),
      cache: new InMemoryCache(),
    });
  }, [authToken]); // 仅token变化时重建客户端

  return { client };
};

2. 解决相同__typename的缓存冲突

针对两个查询返回相同__typename的问题,有两种处理方式:

方式一:自定义缓存ID生成逻辑

在InMemoryCache中配置dataIdFromObject,为相同__typename的对象生成唯一标识:

const cache = new InMemoryCache({
  dataIdFromObject: (object) => {
    // 假设你的共享__typename是"SharedType",结合查询相关的唯一字段生成ID
    if (object.__typename === 'SharedType') {
      // 如果返回结果中有区分字段(比如queryType),用它来生成唯一ID
      return `${object.__typename}:${object.queryType}`;
    }
    // 默认逻辑:用__typename+id生成ID,无id时用随机值临时区分
    return object.id ? `${object.__typename}:${object.id}` : `${object.__typename}:${Math.random()}`;
  },
});

方式二:为单个查询指定缓存键

在useQuery中通过context给每个查询设置唯一缓存键,让Apollo区分不同查询的结果:

useGetUserQuery({
  variables: { id: 'SE' },
  context: {
    cache: {
      key: 'getUser-SE', // 唯一缓存键
    },
  },
  fetchPolicy: 'cache-first', // 强制优先使用缓存
});

useTagsQuery({
  context: {
    cache: {
      key: 'tags-query', // 唯一缓存键
    },
  },
  fetchPolicy: 'cache-first',
});

3. 验证组件挂载情况

检查Test组件是否被多次挂载(比如父组件逻辑导致重复渲染/挂载),可以添加挂载日志确认:

const Test = () => {
  console.log('rendering ....');
  
  useEffect(() => {
    console.log('Test component mounted');
    return () => console.log('Test component unmounted');
  }, []);

  useGetUserQuery({ variables: { id: 'SE' } });
  useTagsQuery();

  return null;
};

如果出现多次挂载/卸载,需要排查父组件的渲染逻辑。

验证结果

修改后,重新运行项目:

  • 网络面板应只出现2次请求(对应两个useQuery)
  • Apollo DevTools缓存面板应能看到两个查询的缓存条目,不再出现覆盖情况

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 19:47:35