How to create a custom useQuery hook with optional configuration options (useQueryOption)❓ #3227
-
Hi all, // Page1
const myData = useCustomHook('some_val', {
onSuccess: () => navigate('/settings'),
//....other valid options.....
})
// Page2
const myData = useCustomHook('some_val', {
onSuccess: () => navigate('/other_route'),
//....other valid options, but different.....
}) What I've TriedInitially my custom hook using type Item = { name: string };
const fetcher = async (): Promise<Item[]> => [{ name: "adam" }, { name: "ben" }];
// has ability to pass optional values to useQuery's `useQueryOptions` object but types are lost ❌
export const useGetData = (randomParam: string, configOptions: UseQueryOptions) => {
const queryResponse = useQuery({
queryKey: [randomParam],
queryFn: fetcher,
...configOptions // ← this object spreading causing problems
});
return queryResponse; // ❌ unfortunately this comes back UseQueryResult<unknown, unknown> , it should be UseQueryResult<Item[], unknown>
}; I was able to get back the type information I needed by casting type Item = { name: string };
const fetcher = async (): Promise<Item[]> => [{ name: "adam" }, { name: "ben" }];
export const useGetData = (randomParam: string, configOptions: UseQueryOptions) => {
const queryResponse = useQuery({
queryKey: [randomParam],
queryFn: fetcher,
...configOptions
}) as UseQueryResult<Item[]>; // ✅
return queryResponse;
}; Is there a way to get this to work without casting? I've tried this (no cast) but the issue seems to be the type Item = { name: string };
const fetcher = async (): Promise<Item[]> => [{ name: "adam" }, { name: "ben" }];
// --------------------------------------------------------------------------------------------↓
export const useGetData = (randomParam: string, configOptions: UseQueryOptions): UseQueryResult<Item[]>; => {
const queryResponse = useQuery({
queryKey: [randomParam],
queryFn: fetcher,
...configOptions
})
return queryResponse; // ❌ unfortunately this comes back UseQueryResult<unknown, unknown> , it should be UseQueryResult<Item[], unknown>
}; Codesandbox: https://codesandbox.io/s/nervous-engelbart-mzygc?file=/src/App.tsx References |
Beta Was this translation helpful? Give feedback.
Replies: 7 comments 13 replies
-
Type inference does not seem to work for export const useGetData = (randomParam: string, configOptions: UseQueryOptions<Item[]>) => {
return useQuery([randomParam], fetcher, {
...configOptions, // ← this object spreading causing problems
});
}; Type inference is great when it works, but sometimes it does not and it can get very complicated which is why I prefer specifying types everywhere. I am not talking about React Query, but TypeScript code in general. |
Beta Was this translation helpful? Give feedback.
-
This is not a type inference issue. Lines 26 to 31 in 250c654 if that doesn't match your actual types, you get mismatches. Passing one generic as @mfpopa suggests will solve most of the issues, but My usual advice would be to not create abstractions that allow passing all query options, but to only provide those that you want consumers to override. Otherwise, the abstraction is likely too wide, and you'd need to juggle a lot of generics like react-query has to do internally :) |
Beta Was this translation helpful? Give feedback.
-
Had a similar situation and I solved it this way by passing the same type used in export const useGetData = (randomParam: string, configOptions?: UseQueryOptions<Item[]>) => {
return useQuery<Item[]>([randomParam], fetcher, options);
}; |
Beta Was this translation helpful? Give feedback.
-
resolved export const useHealth = (options?: QueryOptions<HealthResult, ErrorResponse>) => {
return useQuery(['health'], health, options)
} and export const useUnsubscribe = (
options?: MutationOptions<unknown, ErrorResponse, string>
) => {
return useMutation(unsubscribe, options)
} |
Beta Was this translation helpful? Give feedback.
-
Please …how to write in react js |
Beta Was this translation helpful? Give feedback.
-
In case this is still an issue for anyone that stumbles upon this thread, I resolved this like so: export function useSpecificButCustomizableQuery(
params: Record<string, string>,
queryOptions: Omit<
UseQueryOptions<
TypeOfReturnValue,
unknown,
TypeOfReturnValue,
TypeOfTheQueryKey
>,
'queryKey' | 'queryFn'
> = {}
) {
return useQuery<
TypeOfReturnValue,
unknown,
TypeOfReturnValue,
TypeOfTheQueryKey
>({
...queryOptions,
queryKey: ['your', 'key'],
queryFn: (): Promise<TypeOfReturnValue> => {}
});
} |
Beta Was this translation helpful? Give feedback.
-
I utilized @TkDodo's approach with const useCustomQuery() (options: Pick<UseQueryOptions, 'enabled'> = { enabled: true }) => {
useQuery( queryKey, queryFn, options)
} |
Beta Was this translation helpful? Give feedback.
This is not a type inference issue.
UseQueryOptions
has 4 generics. If you just writeUseQueryOptions
, all default values for the generics are chosen:query/src/reactjs/types.ts
Lines 26 to 31 in 250c654
if that doesn't match your actual types, you get mismatches.
Passing one generic as @mfpopa suggests will solve most of the issues, but
select
won't work and thequeryKey
will not have the correct type in thequeryFunctionContext
.My usual advice would be to not create abstractions …