网站搜索

reactjs - React Query - 结合使用 useQuery 的多个自定义 hook 的属性,并保证类型安全


我有任意数量的自定义钩子,它们返回 useQuery 的查询结果。我想做的是将这些钩子的返回值组合成一个具有以下结构的对象:

{
  data,
  isLoading,
  isFetching,
  isSuccess,
}

data 属性将是一个数组,由组合所有结果上的 data 属性创建。这是我当前的代码:

export function isAnyProperty(
  property: keyof QueryObserverBaseResult<unknown, unknown>,
  ...queries: UseQueryResult<unknown, unknown>[]
) {
  return queries.some((query) => query[property]);
}

export function combineQueryResults<TData extends unknown[], TQueries extends UseQueryResult<TData>[]>(
  ...queries: TQueries
) {
  const data = queries.reduce<TData[]>((acc, query) => {
    if (query.data === undefined) {
      return acc;
    }

    return [...acc, query.data];
  }, []);

  return {
    data,
    isLoading: isAnyProperty('isLoading', ...queries),
    isFetching: isAnyProperty('isFetching', ...queries),
    isSuccess: isAnyProperty('isSuccess', ...queries),
  };
}

当我使用剩余参数时,我无法让 TypeScript 推断 TData 的值,而且我不太确定原因。需要注意的一点是,当 data 是一个 UseQueryResult 数组时,我确实设法让 TypeScript 推断出了正确的类型,只是对于 TData 这一层却做不到。

供参考,我目前从 data 得到的是 unknown[][]。我期望得到的当然是一个包含 UseQueryResult 中 data 值的元组。我以为可能是 reduce 函数去掉所有 undefined 值会影响 TypeScript 推断正确的值——所以我也尝试了一个简单的 map,结果是 data: (unknown[] | undefined)[] ——所以那也不行。

我也尝试了这个:

export function combineQueryResults<TData extends unknown[]>(...queries: UseQueryResult<TData>[]) {
  const data = queries.map((query) => query.data);

  return {
    data,
    isLoading: isAnyProperty('isLoading', ...queries),
    isFetching: isAnyProperty('isFetching', ...queries),
    isSuccess: isAnyProperty('isSuccess', ...queries),
  };
}

但是使用这段代码,TypeScript 只会推断传递给 combineQueryResult 的第一个变量的类型,并导致 类型 'TypeOne[]' 不能赋值给类型 'TypeTwo[]' 的错误。

看起来当与 UseQueryResult 一起使用时,TypeScript 会推断类型 TData,但单独使用时不会:

unknown[]、Vod.ConstructedAsset[] 和 Group.Union[] 都是 TData。

答案#1

我创建了这个函数来合并我的查询:

const combineResults = <T extends object>(
  data: UseQueryResult<T[], unknown>[]
): T[] =>
  data.reduce((acc, curr) => {
    if (!curr.data) return acc;

    return [...acc, ...curr.data];
  }, []);
  
  
//my hook
const useMyHook = (teams: TeamInfo[]) => {
  const result = useQueries(
    //Your queries here...
  );

  return {
    data: combineResults<MyInterface>(result),
    isLoading: result.some((query) => query.isLoading),
    isFetching: result.some((query) => query.isFetching),
  };
};

我希望这能帮到你。