Reputation: 789
I have the following requirements to my table:
My assumptions are as follows:
What I was able to achieve can be seen in this sandbox https://codesandbox.io/s/react-table-infinite-mzkkp?file=/src/MuiTable.js (I cannot provide code here because it is quite large)
So, in general I could make Autosizer, CellMeasurer and List components from react-virtualized to work. I am stuck at inifinite scroll part. I saw the example on official docs, but it seems to be a little bit anti pattern (mutation state directly is not a good thing at all)
So I tried to achieve similar result, however if you could see, my loadMore function fires too early for some reason. It leads to requests being sent on nearly every scroll event.
Any help is much appreciated.
What I tried already:
<TableRow
{...row.getRowProps({
style
})}
component="div"
>
{row.cells.map((cell) => {
return (
<TableCell {...cell.getCellProps()} component="div">
{cell.render("Cell")}
</TableCell>
);
})}
</TableRow>
Also, it is not clear how to render custom filters this way.
Upvotes: 17
Views: 7508
Reputation: 906
I don't have a complete solution for you, but here are a few things that might help you in your quest:
Consider a static header instead of using position: sticky
to keep the header row at the top.
(If you could get everything else in react-inifnite-scrollcomponent to work, this would solve the sticky header problem.)
A method I've used is to adjust for the size of the scrollbar and always include it (even if not needed) - this may or may not work in all cases:
// Header div
<div
style={{ width: 'calc(100% - 0.9em)' }} // accomodate for the width of the scrollbar
>
// header stuff
</div>
// list (simplified for clarity) - here I used react-window
<div>
<AutoSizer>
{({ height, width }) => (
<List
style={{
overflowX: 'hidden',
overflowY: 'scroll', // always display scrollbar
}}
>
{RenderRow} // function to return a complete react-table row
</List>
)}
</AutoSizer>
</div>
And here are some other ideas if that one doesn't work for you: https://stackoverflow.com/a/63412885/6451307.
If you need horizontal scrolling, you might consider the above plus a wrapper to provide the horizontal scrolling separately.
Consider the table as a list of rows for virtualization purposes.
It sounds like you've already tried this some, but I definitely found it easiest to use the list virtualization tools and then render a row of cells for each list item.
You can see an example of that in my code above, too.
Consider an overlay for the loading indicator.
If it works with your UI needs, create a separate element for the loading indicator that overlays the table body (using absolute positioning and turning it on only when it's needed). It could be solid to hide the table data completely or have a translucent background. This would prevent it from causing problems with whatever is going on in the table/list itself.
(Some accessibility actions might need to be taken, too. Like hiding the table data from screen readers when the overlay is up, giving the overlay role="alert"
or similar, etc. so consider that if you go this route.)
If you're not already doing it, consider using div's instead of trying to use table elements to allow for easier styling and more flexible element structure.
react-table adds the correct table roles so you can use div's and your table should still have the correct semantics as long as you apply the react-table functions properly - like {...getTableBodyProps()}
and similar.
Hopefully, one or more of those will help you get closer to your goal. Good luck!
Upvotes: 1
Reputation: 41
I've found the Virtuso package to be a great help for this. Works better out the box for dynamically sized list items.
Upvotes: 2
Reputation: 1201
I know this is not the answer you were looking for, but I recommend using an IntersectionObserver
instead of the virtualize library. react-virtualized is not great for dynamic/responsive sized elements. IntersectionObserver
is a native browser api that can detect when an element enters the viewport, without providing it with element sizes.
You can easily use it with a react library like react-intersection-observer which has an InView
component and a useInView
hook, though that would require wrapping every table cell with InView
. The performance of an IntersectionObserver tends to be better than a scroll event-based solution like react-virtualized, though that might degrade if you have 1000s of observers.
You could also use something like intersection-observer-admin, which will pool all your observers into one instance similar to the react SyntheticEvent. You'd want to integrate that into something like React.useContext
or redux.
Upvotes: 1