axios React Hook的条件使用

cnh2zyt3  于 2023-08-04  发布在  iOS
关注(0)|答案(1)|浏览(106)

我试图 Package useQuery以集中使用它。然而,我遇到的问题是queryFn是在运行时构建的,所以当 Package 在我的自定义钩子中时,我必须根据queryFn是否准备好有条件地返回钩子的promise。这打破了钩子的规则。有人知道如何在我的自定义钩子中正确 Package useQuery吗?当前状态下的代码如下。要查看的主要部分是返回值以及如何设置queryFn。这就是问题的症结所在。

import {
  QueryFunction,
  QueryKey,
  UseQueryOptions,
  UseQueryResult,
  useQuery,
} from "@tanstack/react-query";
import { AxiosRequestConfig, AxiosResponse } from "axios";
import {
  ApiQueryConfig,
  QueryPathParamsType,
  QueryReturnType,
  QueryUrlParamsType,
  useApiClient,
} from "@api";
import { combineQueryKey } from "./utils";
import { useEffect, useState } from "react";

const useApiQuery = <
  T extends ApiQueryConfig<any, Record<string, string>, Record<string, any>>,
  ReturnType extends QueryReturnType<T>,
  PathParamsType extends QueryPathParamsType<T>,
  UrlParamsType extends QueryUrlParamsType<T>
>(
  apiQueryConfig: ApiQueryConfig<ReturnType, PathParamsType, UrlParamsType>,
  pathParams?: PathParamsType,
  urlParams?: UrlParamsType,
  axiosRequestConfig?: AxiosRequestConfig,
  tanstackConfig?: UseQueryOptions<
    AxiosResponse<ReturnType>,
    Error,
    AxiosResponse<ReturnType>,
    QueryKey
  >
): UseQueryResult<AxiosResponse<ReturnType, any>, Error> => {
  const apiClient = useApiClient();
  const [queryFn, setQueryFn] = useState<
    QueryFunction<AxiosResponse<ReturnType, any>> | undefined
  >(undefined);

  const axiosConfigNonOverridable = {
    params: urlParams || {},
  };
  const axiosConfigOverridable: AxiosRequestConfig = {
    timeout: 10 * 1000,
  };
  const mergedAxiosRequestConfig: AxiosRequestConfig = {
    ...axiosConfigOverridable,
    ...(axiosRequestConfig || {}),
    ...axiosConfigNonOverridable,
  };

  const tanstackConfigNonOverridable: typeof tanstackConfig = {
    enabled: !!apiClient && (tanstackConfig?.enabled || true),
  };
  const tanstackConfigOverridable: typeof tanstackConfig = {
    networkMode: "online",
    retry: 2,
    retryOnMount: true,
    staleTime: Infinity,
    cacheTime: 10 * 60 * 1000,
    refetchOnMount: true,
    refetchOnWindowFocus: false,
    refetchOnReconnect: true,
  };
  const mergedTanstackConfig: typeof tanstackConfig = {
    ...tanstackConfigOverridable,
    ...(tanstackConfig || {}),
    ...tanstackConfigNonOverridable,
  };

  const path = pathParams
    ? Object.entries(pathParams).reduce(
        (accPath, [key, value]) => accPath.replace(`{${key}}`, value),
        apiQueryConfig.apiPath
      )
    : apiQueryConfig.apiPath;

  const queryKey = combineQueryKey(
    apiQueryConfig.queryKey.baseQueryKey,
    { ...pathParams, ...urlParams },
    apiQueryConfig.queryKey.dynamicQueryKey
  );

  useEffect(() => {
    if (apiClient) {
      console.log(apiClient);
      setQueryFn(() => apiClient!.get(path, mergedAxiosRequestConfig));
    }
    // We should not use exhaustive deps here. Deps should be intentional.
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [apiClient]);

  if (!queryFn) {
    return { isLoading: true } as UseQueryResult<
      AxiosResponse<ReturnType, any>,
      Error
    >;
  }

  return useQuery<AxiosResponse<ReturnType>, Error>(
    queryKey,
    queryFn!,
    mergedTanstackConfig
  );
};

export { useApiQuery };

字符串

sczxawaw

sczxawaw1#

我同意@kca的评论,你应该考虑使用多个更小的钩子。返回“nothing”的方法是设置enable: true
我做了一些修改建议。最值得注意的是,我用一个常量替换了queryFn状态,并将enabled: false设置为queryFn === undefined

const useApiQuery = <
  T extends ApiQueryConfig<any, Record<string, string>, Record<string, any>>,
  ReturnType extends QueryReturnType<T>,
  PathParamsType extends QueryPathParamsType<T>,
  UrlParamsType extends QueryUrlParamsType<T>
>(
  apiQueryConfig: ApiQueryConfig<ReturnType, PathParamsType, UrlParamsType>,
  pathParams?: PathParamsType,
  urlParams?: UrlParamsType,
  axiosRequestConfig?: AxiosRequestConfig,
  tanstackConfig?: UseQueryOptions<
    AxiosResponse<ReturnType>,
    Error,
    AxiosResponse<ReturnType>,
    QueryKey
  >
): UseQueryResult<AxiosResponse<ReturnType, any>, Error> => {
  const apiClient = useApiClient();

  const axiosConfigNonOverridable = {
    params: urlParams || {},
  };
  const axiosConfigOverridable: AxiosRequestConfig = {
    timeout: 10 * 1000,
  };
  const mergedAxiosRequestConfig: AxiosRequestConfig = {
    ...axiosConfigOverridable,
    ...(axiosRequestConfig || {}),
    ...axiosConfigNonOverridable,
  };

  // move `path` and `queryFn` here so we know the status
  // of `queryFn` before gathering the tanstack config
  const path = pathParams
    ? Object.entries(pathParams).reduce(
        (accPath, [key, value]) => accPath.replace(`{${key}}`, value),
        apiQueryConfig.apiPath
      )
    : apiQueryConfig.apiPath;

  // `queryFn` is directly dependent on `apiClient`, `path` and 
  // `mergedAxiosRequestChanges`, therefore it shouldn't be a state
  const queryFn = apiClient
    ? () => apiClient.get(path, mergedAxiosRequestConfig)
    : undefined;

  const tanstackConfigNonOverridable: typeof tanstackConfig = {
    enabled:
      !!apiClient &&
      // disable the query if we do not have the `queryFn`
      !!queryFn &&
      // check for exactly false, the previous implementation
      // would have `false` replaced with `true` every time
      (tanstackConfig?.enabled !== false),
  };
  const tanstackConfigOverridable: typeof tanstackConfig = {
    networkMode: "online",
    retry: 2,
    retryOnMount: true,
    staleTime: Infinity,
    cacheTime: 10 * 60 * 1000,
    refetchOnMount: true,
    refetchOnWindowFocus: false,
    refetchOnReconnect: true,
  };
  const mergedTanstackConfig: typeof tanstackConfig = {
    ...tanstackConfigOverridable,
    ...(tanstackConfig || {}),
    ...tanstackConfigNonOverridable,
  };

  const queryKey = combineQueryKey(
    apiQueryConfig.queryKey.baseQueryKey,
    { ...pathParams, ...urlParams },
    apiQueryConfig.queryKey.dynamicQueryKey
  );

  const res = useQuery<AxiosResponse<ReturnType>, Error>(
    queryKey,
    // instead of non-null assertion (`!`), we might be better of returning
    // an explicit error if we accidentally end up in that forbidden state
    queryFn || () => Promise.reject(new Error("Missing query function")),
    mergedTanstackConfig
  );

  return {
    ...res,
    // overwrite `isLoading` with `true` while `apiClient` is loading
    isLoading: res.isLoading || !apiClient
};

字符串

相关问题