Typescript - pass queryOptions in custom hook to useQuery
How are we supposed to take useQuery's query options as parameter to our custom hooks and pass it down while maintaining proper typescript type information?
16 Replies
ratty-blushOP•3y ago
I've tried with:
Where
QueryHookParams = export type QueryHookParams = Omit<UseQueryOptions, 'queryKey' | 'queryFn'>;;
However, this has a few issues. First, typescript is confused as it considers the passed object as QueryKey type and not query options thus resulting in an error.
I circumvent that by doing:
which is not ideal.
Second, as I haven't provided the types for the generics, when trying to use the hook like: useCurrencies(); the returned type is unknown instead of the properly infered type.national-gold•3y ago
This is something I've been wondering about as well. Currently, I'm typing the options as
UseQueryOptions<ReturnType,any,ReturnType,any> and it does the trick. It does feel very boilerplaty to do it like this and I wonder if it couldn't be inferred from the return type of the queryFn in any way.ratty-blushOP•3y ago
Yeah, it is quite frustrating.
I don't know, this is such a trivial and general case, I don't know why there aren't any examples of it. How are we supposed to pass the parameters in the first place? One should be able to just spread the incoming props over the
useQuery but the type gets misinterpreted and doesn't allow it.
@TkDodo 🔮 So sorry for tagging you but can you shed some light on the case, please?flat-fuchsia•3y ago
It does allow it, but my take is that you likely don't really want to build a custom hook that allows to pass in all options. It's not a good abstraction, especially not for the example you've shown.
If you want it, your custom hook needs 4 generics.
ratty-blushOP•3y ago
If it is not too insolent of me, can you please show me a working example? I really just want to forward the properties, nothing more. I am not trying to make an abstraction. Returning query object (observer) kills all possibility of abstractions anyway.
flat-fuchsia•3y ago
I'm explaining here how to do it and also why you don't want to do it 🙂
https://twitter.com/TkDodo/status/1491451513264574501
Dominik 🇺🇦 (@TkDodo)
I have been asked a lot lately how to make your own low-level abstraction over useQuery and have it work in #TypeScript. My answer is usually: You don't need it, as those abstractions are often too wide. But there are use-cases for it, so here is my take. Let's break it down ⬇️
Likes
267

Twitter
ratty-blushOP•3y ago
Thank you, I am on it.
flat-fuchsia•3y ago
I'd really like to know why you need it, in your example, where you have a fixed queryFn and queryKey. What's the point of allowing all options like
cacheTime to be passed by consumers? You say you "don't want to create an abstraction", but that's what custom hooks are ...
my guess is you think you need it but you actually don't 🙂ratty-blushOP•3y ago
Yes, I don't need all of them, that's true. I need
enabled, the refetch* flags, the retry flags, the callbacks and select.
I have different refetch needs for the same data. For example, if I present the data in a table I want the refetch on focus enabled. However, when I use the data for a dropdown picker in a form, I don't want it to get refreshed on window focus.
Same underlying data, different needs for it.
Thank you for the input, I appreciate it.
@TkDodo 🔮 Hey, I followed the example in the twitter post you provided. Can you, please, provide an example on how to build a concrete hook on top of it (like usePosts), though? No example was given in the twitter post about that but it is exactly where the question arises.
If I have:
I guess I should:
?
Where QueryHookParams is:
And I should specify the generic types on each and every custom hook I make?
Also, I am not sure what type I should pass for TQueryKey as obviously typeof CURRENCIES_QUERY_KEYS.ALL is a pretty bad idea but it is the only thing that actually gets accepted by typescript. Using unknown[] / any[] results in:
CURRENCIES_QUERY_KEYS is defined as:
flat-fuchsia•3y ago
Show a typescript playground please
ratty-blushOP•3y ago
@TkDodo 🔮 Yes, of course.
ratty-blushOP•3y ago
zhulien-ivanov
CodeSandbox
suspicious-dubinsky-2zyd97 - CodeSandbox
suspicious-dubinsky-2zyd97 by zhulien-ivanov using @tanstack/react-query, react, react-dom, react-scripts
ratty-blushOP•3y ago
options?: QueryHookParams<ICurrency[], unknown, ICurrency[], unknown[]> is the culprit.
unknown[] \ any[] doesn't work for me. Only any does.
Thank you once again for taking the time.flat-fuchsia•3y ago
really not sure why you want it but here it is: https://codesandbox.io/s/silly-tdd-wiby85?file=/src/currencies/use-currencies.hook.ts
TkDodo
CodeSandbox
silly-tdd-wiby85 - CodeSandbox
silly-tdd-wiby85 by TkDodo using @tanstack/react-query, react, react-dom, react-scripts
ratty-blushOP•3y ago
This is exactly how I did it before that.
Not sure why I specify the type or why I introduce the
useQueryHook hook in the first place?flat-fuchsia•3y ago
not sure why there are two abstractions, useQueryHook and useCurrencies that are both super generic and need to pass-through all options