SixtyEight
SixtyEight

Reputation: 2480

How to perform action after all parallel queries are successful in "React-Query"

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

Answers (1)

TkDodo
TkDodo

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

Related Questions