Closed utterances-bot closed 2 years ago
Fantastic!!!
This is a tremendous write up! Thanks!
Good stuff! Thanks 💪
Great post! Hadn't considered using the enabled option for anything else then dependent queries, very neat!
Great write up 🔥
Thanks TkDodo!
Thank you. Super clear! 💡
Good writeup. I wasn't clear before on what is the difference between isFetching and isLoading.
Thanks! Super ❤️👌
Love the way you explain things, couldn't be better ❤️
This! Great article. Clarified a lot of confusions.
Thanks tkdodo for this amazing article. for a server state that can be editable by user, can I setState in onSuccess ? in this way we are not forced to wait for data and pass it as initial state to the component.
for a server state that can be editable by user, can I setState in onSuccess ?
you can, but be aware that onSuccess
is called for every successful background refetch as well, thus calling setState
. I would still stick to a clear separation as outlined here: https://tkdodo.eu/blog/practical-react-query#keep-server-and-client-state-separate
This is exactly what I am looking for. Thank you tkdodo!
I wrapped useQuery inside custom hook and use it in many places in my code. One thing I noticed is that I log the success event in onSucess
handler, so every time the fetch succeed, all the observers call onSuccess and the success event is log multiple times. Is there a way to only send the log once?
Is there a way to only send the log once?
we have on open PR to add a global onSuccess handler to the queryCache. Feel free to try it out and provide feedback: https://github.com/tannerlinsley/react-query/pull/2404
An amazing write-up! A gem. Very rare do I see articles having this level of quality, packed with practical insights wrapped in logically tight prose that are not only easy but a joy to consume.
thank you very much @phutngo, your comment truly made my day ❤️
I've been loving this series, so much great info! Question on custom hooks, is there any reason to destructure in the custom hook to return just what you plan on using or should you just return useQuery
and destructure when you use it?
const useTodos = () => useQuery(getTodos)
vs
const useTodos = () => {
const {data, error, isLoading} = useQuery(getTodos)
return {data, error, isLoading}
}
Awesome!! Great article!
@jmsims2 I personally like to return everything from useQuery
, to keep the return values the same for every custom hook. Especially if you use tracked queries, every consumer can decide what they need and react-query will render optimize accordingly. But generally speaking, it doesn't matter much and comes down to personal preference :)
Thank you very much for this article, it is very clear ! 👍
Amazing article, this was very helpful. I am using react-query for a very large scale application React application. My problem is, I would like to run some function on the onSuccess
and onSettled
events of the queries and mutations. Some of them containe generic logic while others are specific.
I would like to create a base query/mutation hook and extend these for the feature-specific queries/mutations, to avoid duplication. However, I'm still not sure if this is the right way to go.
Are there any pointers you can give on handling this issue?
@AleeyCreative what kind of logic are we talking about? Please keep in mind that the callbacks will be executed on every background update, and they will also execute once per observer. So if you use the same useQuery
hook (or custom hook that wraps useQuery
) multiple times, the callbacks will execute multiple times
As amazing as it is practical. I would love to submit a PR to fix a typo I spotted if I could. @TkDodo.
@Miravicson yes please do, the blog is open source: https://github.com/TkDodo/blog
@TkDodo, I created a PR. now the article is truly perfect 😀. https://github.com/TkDodo/blog/pull/50
Thank you for the great article. I just started to use react-query for my new project, and I have some questions about how to use it. I have an OAuth2.0 flow for my authentication, so it is redirect-based. When I get the code in my redirect page I request for a token. Tokens are valid for 15 minutes. The API also supports refresh tokens. Right now I handle the authentication part using Redux Toolkit. I request the token after redirect, save it in Redux store using createAsyncThunk function. Then I set an interval with the interval time of 14 minutes to request for refresh tokens. I also store token data and its expiration time in local storage, and whenever token data changes I update the local storage. So if the user refreshes the page, they are automatically logged in without sending a request for token.
First of all, I need to know if token information is considered server state? Does it make sense to move this logic into react-query? Is that even feasible? Because I don't need background refetch when screen is focused or data is stale, I just need that refresh token request every 14 minutes, and I don't want any additional and unnecessary requests for tokens. If it makes sense to do it, how should I do it, reading and writing to local storage and just use the token wherever in my app?
@alimirmohammad It's an interesting question. getting a refresh token could be seen as mutation, given that it creates a token (resource). Once you have it, do you really need to distribute it to all consumer? You could also attach it directly as a header to your "network request layer", e.g. axios.create
. That could be a custom hook in itself.
Dan Abramov has a great example of making a useInterval
hook: https://overreacted.io/making-setinterval-declarative-with-react-hooks/
if you wind up with a query
, I would set staleTime to Infinity (and maybe cacheTime as well), and then use the refetchInterval
option to refetch every 14 minutes.
@TkDodo, Thank you for the hint. I actually wound up using both mutations and queries. Because a mutation data is not meant to be read from several places as far as I know. Also, there is no method to set a mutation data in the cache, I need this for my auto login when the token is not expired. So I created a dummy query with a queryFn which is a promise that resolves to some object. And onSuccess of my mutations for token and refresh token I update the local storage and I set the data for the query. Also when app starts, I check and if there is a valid token, I set data for the query.
For using the token, I created a wrapper for the Fetch API (I don't use Axios because I need service-workers), and attach the token to my requests. For this, I exported the queryClient in a file, and imported it here, to read the token data.
I know it sounds imperative, but it works.
Also I created several custom hooks to get different parts of token data, with duplicated code, because TypeScript had a hard time inferring the type of the select option.
Amazing! There's a lot to unpack in this article...
Hi there, the Dependant Queries link has changed on the official doc. Here is the correct one: https://react-query.tanstack.com/guides/dependent-queries#_top
@TkDodo I left a question for you on stackoverflow on how to implement long polling using react-query interesting enough there is nothing on the web related to the subject.
" Would like to ask for suggestions on implementing long Polling with useQuery. i.e. send a new request only when response comes back, and request will wait on the server until new events or some changes happens and maybe some time out. "
@Moe2610 thanks, I've seen the question, but I don't have an answer yet. I haven't worked with long polling yet and I don't immediately see how it would integrate with react-query, as there won't really be any smart refetching (as the connection is always open?). So maybe a simple useEffect + context to distribute the data would be sufficient...
@TkDodo thanks for replay. The connection is not going to be always open. The process is as following:
I think this covers most of the required behavior. Could achieve with recursive fetch but want to use react-query eco system to avoid boilerplate and for consistency across the application.
The connection is not going to be always open.
Yes, I was phrasing that poorly, because always open would be websockets. I meant "de-facto" always open, because once you receive something and the server closes the connection, the client should open a new one immediately, like you outlined.
I think my reasoning still stands though. If we were to implement that from within the queryFn, the query would essentially always be in loading / fetching state. And we would need to turn off all retries. So similar as to React Query and WebSockets, we could keep that a bit outside of / orthogonal to RQ.
lovely article 👏🏽
Hi, I have a question about "initial-form-data" example:
const { data } = useQuery('key', queryFn, { staleTime: Infinity })
Imagine, there are other queries with the same key on the page:
const { data } = useQuery('key', queryFn)
Those other queries do background re-fetches which, at some point, yield new data.
Will that new data appear in the first query with { staleTime: Infinity }
?
How do staleTime
and cacheTime
work when there are multiple useQuery
with the same key but different values for those options?
How do staleTime and cacheTime work when there are multiple useQuery with the same key but different values for those options?
that doesn't really make sense, which is why it's best to abstract them away into custom hooks. There is only one cache entry, so you can't have multiple cacheTimes
. I guess the longer one wins?
Thanks for the prompt answer!
What about staleTime
? Is it the same?
And what about other options? Can't I have 2 queries on the same page with the same key but different options?
And what about other options? Can't I have 2 queries on the same page with the same key but different options?
you can, but it only makes sense for options that are specific to observers. For example, you can have to queries with different select
, because every observer can pick their own things from the data via select
.
you can also have two different staleTimes, because technically, that's an observer prop. As long as only one query is mounted, that staleTime is taken. However, once both are mounted, what should happen? At the end of the day, there is only one query to mark as stale. So we run two timers, and when the first one (the shorter one) finishes, we mark the cache entry as stale.
For typescript, I was wondering that if a custom hook sacrifices the ability to set options
on every invokation (implicitly) of useQuery
? I can't find a way to easily and correctly type options
as if I:
/* I want the options is infered */
const useTodosQuery = (state: State, options: any) => {
/*
* Here the signature of useQuery is solved, options too.
* Could it infer the type back to type of options of useTodosQuery?
*/
useQuery(['todos', state], () => fetchTodos(state), options)
}
@QzCurious there are definitely ways to make it work, but it is often not the right abstraction. For example, options also contains a QueryKey
, but you have that hardcoded in your example, so it wouldn't make sense to let the user of the custom hook pass that in as an option. Same goes for things like cacheTime
or staleTime
, where it doesn't make sense if multiple hook calls use different value.
To make everything work, you need to juggle 4 generics like react-query does it internally. You can read more in this discussion.
All of this is why I like to keep my custom hooks small in interface. Like, usually something like this:
const useTodosQuery = (state: State, options: { enabled: boolean, onSuccess: (todos: Todos) => void }) => {
useQuery(['todos', state], () => fetchTodos(state), options)
}
or whatever other props I really need at the moment. Sometimes, this means I have to amend the options, but that doesn't happen very often. select
is one I use a lot, so that would be, in TypeScript:
const useTodosQuery = <T = Todos>(state: State, select?: (todos: Todos) => T) => {
useQuery(['todos', state], () => fetchTodos(state), { select })
}
But I don't think I've used a custom hook that needed more than 3 props or so. My usages of one query are mostly the same :)
@TkDodo Thanks for your advice. Great to know that in practical aspect of view, some of options are more likely to be "static" options. And only a few of options will be needed (at caller level).
Thanks for a great article!
I'd like to ask what do you mean by:
You can keep all usages of one query key (and potentially type definitions) in one file.
If there is a mutation that updates e.g. the data under the todos
key it will need to use the same query key as the query that downloads the todos. Would you suggest to write such a mutation in the same file?
Would you suggest to write such a mutation in the same file?
Yes, that's what I usually do. Even though I name it queries.ts
, it often also contains mutations. It contains all react-query
related things :)
Practical React Query | TkDodo's blog
Let me share with you the experiences I have made lately with React Query. Fetching data in React has never been this delightful...
https://tkdodo.eu/blog/practical-react-query