Reputation: 2480
In React-Query we can use the useQueries hook to perform parallel queries (mostly like using Promise.all): https://react-query.tanstack.com/guides/parallel-queries
Every Query has a set of options that we can use, on of them is the onSuccess
and can be used as below:
const data = useQueries([{
queryKey: 1,
queryFn: () => axios.get(...),
onSuccess: data => ... do something,
},
{
queryKey: 2,
queryFn: () => axios.get(...),
onSuccess: data => ... do something,
},
])
But what I really need is to be able to perform an action only when all the queries all successful. Mostly like:
Promise.all(fn).then(...)
and use that data to run a function afterwards.
I came up with a few things like
1 - pushing all the successful queries to a useState()
onSuccess: (data) => setData((prevData) => [...prevData, data])
and setup a useEffect:
useEffect(() => {
// run function
}, [data])
But this won't work because it will fire every time one of the queries is successful. Probably will break due to an infinite loop.
2 - Making a condition for when all are successful:
if (data.every(num => num.isSuccess === true)) {
// do something
}
This might work but it looks clumsy to me. Wondering if theres a better and more performant method out there.
Upvotes: 7
Views: 7910
Reputation: 28793
Wondering if theres a better and more performant method out there.
I don't think there is. useQueries
is very generic and unopinionated. For some use-cases, it makes sense to show e.g. a loading spinner until all requests are finished, and for others it make more sense to show data as soon as one request is finished. That's why checking with data.every
or data.some
seems like a good idea.
For rendering things, this is rather easy, for actual side-effects, there is no callback specifically for that, so I think you'd need:
const allSuccess = data.every(num => num.isSuccess === true)
React.useEffect(() => {
if (allSuccess) {
// do your side-effect here
}
}, [allSuccess])
Depending on your use-case, it might make more sense to check for data !== undefined
instead of isSuccess. If a background refetch fails, the query will go to error
state, but it will still have (stale) data in the cache. So to only run the effect "once" when everything completes, I would do this:
const allSuccess = data.every(num => num.data !== undefined)
Upvotes: 8