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

如何自定义React-Query Hook为所有查询自动附加AccessToken?

问题描述

我花了8小时研究这个问题却陷入困境。本以为创建useAuthenticatedQuery Hook,在queryFn中自动传入AccessToken会很简单,但TypeScript却带来极大困扰。

目前我在所有页面中使用如下默认查询:

const {accessToken} = useAuth()
const subscriptionQuery = useQuery({
  queryKey: ["subscription", accessToken],
  queryFn: () => {
    return api.getSubscription(accessToken);
  },
});

我不想每次手动将AccessToken加入queryKey,也不想每次都调用useAuth Hook获取AccessToken。

以下是我接近目标的代码,但TypeScript报错严重:

const useAuthenticatedQuery = (options: UseQueryOptions) => {
  const { accessToken } = useAuth();

  return useQuery({
    ...options,
    queryKey: [options.queryKey, accessToken],
    queryFn: (context) => {
      return options && options.queryFn && options.queryFn({ ...context, accessToken });
    },
  });
};

将AccessToken加入queryKey时TypeScript抛出如下错误:

No overload matches this call.
  The last overload gave the following error.
    Argument of type '{ queryKey: (string | QueryKey | undefined)[]; queryFn: (context: any) => unknown; context?: Context<QueryClient | undefined> | undefined; enabled?: boolean | undefined; ... 31 more ...; meta?: QueryMeta | undefined; }' is not assignable to parameter of type 'QueryKey'.
      Object literal may only specify known properties, and 'queryKey' does not exist in type 'readonly unknown[]'.ts(2769)

我目前最接近的实现如下:

const useAuthenticatedQuery = <
  TQueryFnData = unknown,
  TError = unknown,
  TData = TQueryFnData,
  TQueryKey extends QueryKey = QueryKey
>(
  key: TQueryKey,
  fn: (accessToken: string) => QueryFunction<TQueryFnData, TQueryKey>,
  options: UseQueryOptions<TQueryFnData, TError, TData, TQueryKey> = {}
) => {
  const { accessToken } = useAuth();

  return useQuery(key, fn(accessToken), {
    ...options,

    onError: (error) => {
      options.onError?.(error);
    },
  });
};

但调用时写法繁琐,还没解决AccessToken加入queryKey的问题:

const subscriptionQuery = useAuthenticatedQuery(
  ["subscription"],
  () => (accessToken) => api.getSubscription(accessToken),
  {}
)

需要更优的实现方式。


解决方案

我们可以通过泛型约束严格对齐React Query的类型,同时自动注入accessToken到queryKey和queryFn中,保持调用简洁。

最终实现代码

import { useQuery, UseQueryOptions, QueryKey, QueryFunctionContext } from '@tanstack/react-query';
import { useAuth } from './your-auth-module-path';

type AuthenticatedQueryFn<TQueryFnData, TQueryKey extends QueryKey> = (
  context: QueryFunctionContext<TQueryKey>,
  accessToken: string
) => TQueryFnData | Promise<TQueryFnData>;

const useAuthenticatedQuery = <
  TQueryFnData = unknown,
  TError = unknown,
  TData = TQueryFnData,
  TQueryKey extends QueryKey = QueryKey
>(
  options: Omit<UseQueryOptions<TQueryFnData, TError, TData, TQueryKey>, 'queryFn'> & {
    queryKey: TQueryKey;
    queryFn: AuthenticatedQueryFn<TQueryFnData, TQueryKey>;
  }
) => {
  const { accessToken } = useAuth();

  return useQuery({
    ...options,
    // 自动将accessToken追加到queryKey末尾
    queryKey: [...options.queryKey, accessToken] as TQueryKey extends (infer U)[] ? [...U, string] : never,
    // 注入accessToken到queryFn参数
    queryFn: (context) => options.queryFn(context, accessToken),
  });
};

调用方式

现在调用变得简洁,无需手动处理accessToken:

const subscriptionQuery = useAuthenticatedQuery({
  queryKey: ["subscription"],
  queryFn: (_, accessToken) => api.getSubscription(accessToken),
});

关键说明

  1. 泛型约束:通过Omit去掉原UseQueryOptions中的queryFn,替换为自定义的包含accessToken参数的AuthenticatedQueryFn,确保类型安全。
  2. queryKey自动拼接:利用类型断言将accessToken追加到传入的queryKey后,同时保持类型兼容性。
  3. 自动注入accessToken:在内部调用useAuth获取accessToken,并传递给自定义的queryFn,调用方无需重复获取。

这样既解决了手动添加accessToken到queryKey的问题,也避免了重复调用useAuth,同时满足TypeScript的类型检查要求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 22:30:48