ive been working on this and stumping myself for actually 8 hours today. I figured it would be pretty easy to create a useAuthenticatedQuery hook to simply respond with the access token in the queryFn, but with typescript it has been a total nightmare. Any help would be appreciated
const {accessToken} = useAuth()
const subscriptionQuery = useQuery({
queryKey: ["subscription", accessToken],
queryFn: () => {
return api.getSubscription(accessToken);
},
});
This is the default query that im using out of the box in all of my screens. I dont want to add the access token to the key every time manually, and I dont want to call the useAuth hook for the access token every time.
this code is close to what i want to accomplish (roughly) but typescript has made it hell.
const useAuthenticatedQuery = (options: UseQueryOptions) => {
const { accessToken } = useAuth();
return useQuery({
...options,
queryKey: [options.queryKey, accessToken],
queryFn: (context) => {
return options && options.queryFn && options.queryFn({ ...context, accessToken });
},
});
};
I understand that i could not attach the access token as context, and rather call context.queryKey and get it from that array, but I cannot add the access token as a query key without typescript throwing a fit. (err below)
No overload matches this call.
The last overload gave the following error.
Argument of type '{ queryKey: (string | QueryKey | undefined)[]; queryFn: (context: any) => unknown; context?: Context<QueryClient | undefined> | undefined; enabled?: boolean | undefined; ... 31 more ...; meta?: QueryMeta | undefined; }' is not assignable to parameter of type 'QueryKey'.
Object literal may only specify known properties, and 'queryKey' does not exist in type 'readonly unknown[]'.ts(2769)
Any help is appreciated
This is the closest ive gotten
const useAuthenticatedQuery = <
TQueryFnData = unknown,
TError = unknown,
TData = TQueryFnData,
TQueryKey extends QueryKey = QueryKey
>(
key: TQueryKey,
fn: (accessToken: string) => QueryFunction<TQueryFnData, TQueryKey>,
options: UseQueryOptions<TQueryFnData, TError, TData, TQueryKey> = {}
) => {
const { accessToken } = useAuth();
return useQuery(key, fn(accessToken), {
...options,
onError: (error) => {
options.onError?.(error);
},
});
};
but that requires the function to look like this when called
const subscriptionQuery = useAuthenticatedQuery({
["subscription"],
() => (accessToken) => api.getSubscription(accessToken),
{},
})
Which is not only ugly, but also still doesnt solve the problem of the access token in the keys, and im posituive theres a better way
I think the answer is in this tweet: https://twitter.com/TkDodo/status/1491451513264574501
I've implemented it like this:
function useApi<
TQueryKey extends [string, Record<string, unknown>?],
TQueryFnData,
TError,
TData = TQueryFnData
>(
queryKey: TQueryKey,
fetcher: (params: TQueryKey[1], token: string) => Promise<TQueryFnData>,
options?: Omit<
UseQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
"queryKey" | "queryFn"
>
) {
const { getToken } = useAuth();
return useQuery({
queryKey,
queryFn: async () => {
const token = await getToken();
return fetcher(queryKey[1], token);
},
...options
});
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With