Reputation: 23
A lot of queries in my project take the same params, it's a dashboard app. I'm trying to build a wrapper so I don't have to keep getting these params from my contexts.
The code I'm trying right now is
interface IUseLazyQuery {
<
TQueryFnData = unknown,
TError = unknown,
TData = TQueryFnData,
TQueryKey extends QueryKey = QueryKey
>(
queryKey: TQueryKey,
queryFn: QueryFunction<TQueryFnData, TQueryKey>,
options?: Omit<
UseQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
"queryKey" | "queryFn" | "initialData"
> & {
initialData?: () => undefined;
}
): ReturnType<typeof useQuery<TData, TError>> & {
fetch: Function;
};
}
type IUseLazyQueryOptions<
TQueryFnData = unknown,
TError = unknown,
TData = TQueryFnData,
TQueryKey extends QueryKey = QueryKey
> = Omit<
UseQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
"queryKey" | "queryFn" | "initialData"
> & {
initialData?: () => undefined;
};
export const useLazyQuery: IUseLazyQuery = <
TQueryKey extends QueryKey,
TQueryFnData = unknown,
TError = unknown,
TData = TQueryFnData
>(
queryKey: TQueryKey,
queryFn: QueryFunction<TQueryFnData, TQueryKey>,
options?: IUseLazyQueryOptions<TQueryFnData, TError, TData, TQueryKey>
) => {
const [enabled, setEnabled] = useState(false);
const resolveRef = useRef<(value: unknown) => void>();
const query = useQuery(queryKey, queryFn, {
...options,
enabled: enabled && (options?.enabled === undefined || options?.enabled),
onSettled: () => {
if (resolveRef.current) {
resolveRef.current(null);
resolveRef.current = undefined;
}
},
});
useEffect(() => {
setEnabled(false);
}, [query.data]);
return {
fetch: () => {
setEnabled(true);
return new Promise(async (resolve) => {
resolveRef.current = resolve;
while (resolveRef.current) {
await new Promise((resolve) => setTimeout(resolve, 500));
}
});
},
...query,
};
};
export const useFilteredRequest = <
TQueryFuncParams extends object,
TQueryKey extends QueryKey = QueryKey,
TQueryFnData = unknown,
TError = unknown,
TData = TQueryFnData
>(
name: string,
func: (params: TQueryFuncParams) => TQueryFnData,
params: TQueryFuncParams,
options: IUseLazyQueryOptions<TQueryFnData, TError, TData, TQueryKey>
) => {
const { category, classification, selectedBrands } = usePageFilters();
const { filterRequestParams } = useFilterContext();
const { dataFinalStr, dataInicialStr } = useDateFilterContext();
const filterParams = useMemo(() => {
return {
marcas: selectedBrands.join(","),
classificacoes: classification,
categorias: category,
data_final: dataFinalStr,
data_inicio: dataInicialStr,
...filterRequestParams,
};
}, [
selectedBrands,
classification,
category,
dataFinalStr,
dataInicialStr,
filterRequestParams,
]);
const queryFuncParams = useMemo(() => {
return { ...params, ...filterParams };
}, [params, filterParams]);
return useLazyQuery(
[name, ...Object.values(queryFuncParams).sort()],
() => func(queryFuncParams),
options
);
};
I'm getting a type error :
Argument of type '(string | null | undefined)[]' is not assignable to parameter of type 'TQueryKey'.
'(string | null | undefined)[]' is assignable to the constraint of type 'TQueryKey', but 'TQueryKey' could be instantiated with a different subtype of constraint 'readonly unknown[]'
on
return useLazyQuery(
[name, ...Object.values(queryFuncParams).sort()],
() => func(queryFuncParams),
options
);
[name, ...Object.values(queryFuncParams).sort()]
is resolving to (string | null | undefined)[]
but somehow that conflicts with readonly unknown[]
Tried changing TQueryKey type around but nothing works
Upvotes: 2
Views: 265
Reputation: 29056
This is why I always recommend to not build such low level abstractions over useQuery. The useLazyQuery
wrapper is fine, for useFilteredRequest
, you can't provide a generic of TQueryKey
from the outside, because it could be arbitrarily instantiated. The function useFilteredRequest
itself creates the QueryKey, so the type is relatively fixed, however, you still need to provide that type to the QueryOptions:
- options?: IUseLazyQueryOptions<TQueryFnData, TError, TData, TQueryKey>
+ options?: IUseLazyQueryOptions<TQueryFnData, TError, TData, (string | null | undefined)[]>
Here's a working TypeScript playground
Upvotes: 2