javascriptreactjsreact-query

useQueries dependent on another Query, whose data is with 3 levels depth


I am facing a situation where I will first use a useQuery to fetch an object, which contains multiple URLs three levels deep. And then, I'll use a useQueries to fetch all those URLs. And example given Dependent Queries cannot handle this situation.

For example:

// Get the users ids
const { data: userIds } = useQuery({
  queryKey: ['users'],
  queryFn: getUsersData
})

const usersMessages = useQueries({
  queries: userIds ?
     userIds.map(id => {
        return {
          queryKey: ['messages', id],
          queryFn: () => getMessagesByUsers(id),
        };
      })
  : [],
})

The above code works if userIds is like: [ url1, url2, url3, url4 ] . However, in my case, the userIds has the following structure:

[
  {'name':'A', 'id': [url1, url2]}, 
  {'name':'B', 'id': [url3, url4]}
]

and my objective is to fetch from all URLs.

I can't change the first query using "select" to change the format of the returned data because I am collaborating with others, and changing that would incur many other refactors.

I tried to first get userIds, and then decompose it to form a new array that only contains all the URLs, give this new array to the second useQueries and meanwhile set dependency on !isLoading of the first query, which does not work. The code enters in to a loop and throw an error "Rendered more hooks than during the previous render".


Solution

  • From the link you provided there is a select prop so you can manipulate the returned data. Try something like this

    const { data: userIds } = useQuery({
      queryKey: ['users'],
      queryFn: getUsersData,
      select: users => users.reduce((ids, user) => {
        user.id.forEach(id => ids.push(id);
        return ids; 
      }, []),
    })
    

    This will flatten the id array in each user into one array

    In case you don't want to change the first query, maybe create a new one? Or you could modify the second like this

    const usersMessages = useQueries({
      queries: userIds ?
         userIds.reduce((ids, user) => {
          user.id.forEach(id => ids.push(id);
          return ids; 
         }, []).map(id => {
            return {
              queryKey: ['messages', id],
              queryFn: () => getMessagesByUsers(id),
            };
          })
      : [],
    })