T
TanStack•3y ago
equal-jade

useQueries with infinite queries

I am rendering a dynamically generated tree (e.g. file browser), where every tree node is fetched from an API (I.e. I don't know how many root nodes I will start with, or how many children each node will have). The implementation is more or less like this:
// useTree hook
const [openNodeIds, setOpenNodeIds] = useState([]);
const results = useQueries(openNodeIds.map((nodeId) => ({ queryKey, queryFn: () => fetchChildNodes(nodeId) }));
const nodes = results.map((result) => getNodesFromResult(result));
return nodes;
// useTree hook
const [openNodeIds, setOpenNodeIds] = useState([]);
const results = useQueries(openNodeIds.map((nodeId) => ({ queryKey, queryFn: () => fetchChildNodes(nodeId) }));
const nodes = results.map((result) => getNodesFromResult(result));
return nodes;
Works, great. However, due to the size of some of these API responses, I want to make use of infiniteQuery, where I load the first ~100, and offer the user a button to "load more". Effectively, I'm looking for the useInfiniteQueries hook, which from my humble research, does not exist: https://stackoverflow.com/a/74246920/16699607 So, without that holy grail hook officially existing, does anyone have any ideas for how to implement such a task: Run a dynamic number of infinite queries? Hacks/workarounds very welcome.
3 Replies
equal-jade
equal-jadeOP•3y ago
I'm trying a strategy where for each openNodeId, I render a component that doesn't actually render anything (returns null), but makes does useInfiniteQuery. I look to the QueryClient to get the queryData for each item to aggregate all the responses. This feels wrong but I actually might get this working. Someone stop me by suggesting something better 🫠
ambitious-aqua
ambitious-aqua•3y ago
Just to clarify, you need to fetch an infinite query for every node? So that means every node manages its own pagination? (every node will have a "load more" button for its children?). Using a sub-component per node, each calling a useInfiniteQuery is what first came to mind as well. Not sure about the second part though (looking at the QueryClient to get the queryData for each item to aggregate all the responses), where do you need the aggregated data? If you can share the code or a sandbox that'd help a lot 🙂
equal-jade
equal-jadeOP•3y ago
I got this working via my above strategy. One detail I omitted which is kind of critical to understanding the constraints: the tree is virtualized. This means that there can be parent nodes with long lists of children, where the parent node component is not rendered, while some its children are. This detail is important because it prevents just placing a useInfiniteQuery in the node component themselves, because if you need to fetchNextPage, your component might not even be rendered at the time. SO, that explains why I was trying to use useQueries and useInfiniteQuery in tandem with each other. What I ended up doing is mapping through the open node IDs and for each open node, rendering a NodeResolver component which - returns null (renders nothing in DOM) - uses useInfiniteQuery - subscribes to a zustand store to know when to fetchNextPage The centralized hook grabs all query data using useQueryClient, creates nodes from the API responses, and builds the tree to be rendered. This is essentially how I've managed to run an unknown/dynamic number of useInfiniteQuery hooks without making use of useQueries.

Did you find this page helpful?