I have been working with React Query and React Table recently to achieve a multi-level table structure with expandable rows, where each row should do lazy loading when clicked on. It's for product group hierarchy, which looks like this:
unit-1 (clicking fetches category data lazily)
category-1 (clicking fetches product data lazily)
product-1
product-2
category-2
product-3
unit-2
category-3
product-4
product-5
I have two options for the solution:
Sub components lazy load example for React Table (https://react-table.tanstack.com/docs/examples/sub-components-lazy) This focuses on only one level of depth, so I am not sure how to make it multi-level due to the fact that I am still not very familiar with the React Table API and it seems like the example was not created with that purpose. I tried making it multi-level but I had difficulty in passing some row properties around like (.isExpanded, .id, etc)
A post about manipulating the data by each row click so that rest is handled automatically by the table (https://nafeu.medium.com/lazy-loading-expandable-rows-with-react-table-cd2fc86b0630 - GitHub ex: https://github.com/nafeu/react-query-table-sandbox/blob/main/src/ReactTableExpanding.jsx) This seems to be a more declarative way to handle things. (We fetch new data, merge it with the existing data and the rest is handled) but I have some concerns like if updating the whole table data causes unnecessary renders. Another thing is since we don't render a sub-component here as in the first one, the API endpoint should be arranged to work with a click event. It's not like having a sub-component that is responsible for fetching its own data when it's mounted.
As a note for the table example above, I was planning to use a single API endpoint that takes params like:
?level=root&id=0 (initial units list data for the table on page load)
?level=unit&id=2 (returns categories of unit-2)
...
But this logic doesn't seem enough for supporting fetching on row click so I guess I'll need at least two endpoints if I follow the second approach.
Thanks for reading, and any ideas are welcome! Have a nice day.
As an update, I could make things work mostly with the second option above. As a note on the API endpoints, I could use the same API endpoint and the same hook twice with different parameters. (this part is a bit project specific of course but maybe it gives an idea to the ones having similar scenarios)
To get the initial data for the table, I give hardcoded parameters to the hook, so that the table is rendered with the initialData on page load.
const { data: initialData, isLoading } = useGetLevels(
{
level: 'root',
id: [0],
...
}
);
In the second one, I use enabled: false, and when a row is clicked on I call refetch(), so additional data is fetched and merged with the initial data to update the table and the row is expanded accordingly:
const {
data: partialData,
refetch,
} = useGetLevels(
{
level,
id,
...
},
false
);
and the useGetLevels look like this:
function useGetLevels(
filterParams,
enabled
) {
return useQuery(
['levels', filterParams],
() => fetchLevels(filterParams),
{
enabled,
}
);
}