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

如何结合graphql-codegen使用@tanstack/react-query的useQueries钩子?

问题描述

我已经使用graphql-codegen数月,这是一款很棒的工具。我一直将它与@tanstack/react-query的useQuery和useMutation钩子配合使用。

如今我希望将其与useQueries结合使用——我会收到一组动态(数量不确定)的同类型ID,需要通过查询GraphQL API获取对应数据。由于递归问题,无法在服务器端处理,必须在客户端完成,因此需要批量执行单个查询,这也是我想使用useQueries的原因。

看起来graphql-codegen与useQueries之间没有互操作性,是否属实?如果是的话,推荐采用哪种模式来并行运行多个查询?

我的手写实现(未用生成代码):

import { useQueries } from '@tanstack/react-query';
import { gql } from 'graphql-request';

import { graphQLClient } from '../../graphQLClient';

export const useGetSomeStuff = (ids: string[]) =>
  useQueries({
    queries: ids.map((id) => ({
      queryKey: ['getSomeStuff', id],
      queryFn: async () =>
        graphQLClient.request(
          gql`
            query GetSomeStuff($id: MongoObjectId!) {
              oneStuff(id: $id) {
                ...SomeFragment
              }
            }
          `,
          { id }
        ),
    })),
  });

graphql-codegen生成的代码:

export type GetSomeStuffQueryVariables = Exact<{
  id: Scalars['MongoObjectId'];
}>;

export type GetSomeStuffQuery = {
  __typename?: 'Query';
  // ... 生成的查询字段
};

export const GetSomeStuffDocument = `
    query GetSomeStuff($id: MongoObjectId!) {
  oneStuff(id: $id) {
    ...SomeFragment
  }
}`;
export const useGetSomeStuffQuery = <
  TData = GetSomeStuffQuery,
  TError = unknown
>(
  client: GraphQLClient,
  variables: GetSomeStuffQueryVariables,
  options?: UseQueryOptions<GetSomeStuffQuery, TError, TData>,
  headers?: RequestInit['headers']
) =>
  useQuery<GetSomeStuffQuery, TError, TData>(
    ['GetSomeStuff', variables],
    fetcher<
      GetSomeStuffQuery,
      GetSomeStuffQueryVariables
    >(client, GetSomeStuffDocument, variables, headers),
    options
  );
解决方案

首先明确:graphql-codegen的@tanstack/react-query插件默认确实只生成基于useQuery/useMutation的钩子,但这不代表两者没有互操作性——你完全可以利用生成的类型、查询文档和fetcher函数来配合useQueries使用,既保留类型安全,又实现并行查询。

具体实现方式

你可以直接复用生成的GetSomeStuffDocument、GetSomeStuffQueryVariables、GetSomeStuffQuery类型,以及生成代码里用到的fetcher函数(通常这个函数会在生成的文件里或者你配置的utils文件中导出)。重构后的代码如下:

import { useQueries } from '@tanstack/react-query';
import { graphQLClient } from '../../graphQLClient';
// 导入生成的类型、查询文档和fetcher
import { 
  GetSomeStuffDocument, 
  GetSomeStuffQueryVariables, 
  GetSomeStuffQuery,
  fetcher
} from './generated/graphql';

export const useGetSomeStuff = (ids: string[]) =>
  useQueries({
    queries: ids.map((id) => {
      const variables: GetSomeStuffQueryVariables = { id };
      return {
        queryKey: ['GetSomeStuff', variables], // 和生成的useQuery保持一致的queryKey
        queryFn: () => fetcher<GetSomeStuffQuery, GetSomeStuffQueryVariables>(
          graphQLClient, 
          GetSomeStuffDocument, 
          variables
        ),
        // 可以根据需要添加其他useQuery选项,比如enabled、staleTime等
      };
    }),
  });

为什么这么做?

  • 避免重复手写gql查询语句,减少维护成本
  • 完全复用生成的类型,保证变量和返回值的类型安全
  • 保持和生成的useGetSomeStuffQuery一致的查询逻辑,避免出现不一致的问题

补充建议

如果后续服务器端支持批量查询(比如提供一个接受ID数组的查询接口),那直接用单个useQuery请求所有数据会更高效,但如果确实只能客户端并行查询,上面的方案就是最优解。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 22:45:30