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),
};
};
我希望这能帮到你。
