Material-ui: [docs] Table virtualized

Created on 17 Jul 2017  ·  55Comments  ·  Source: mui-org/material-ui

@oliviertassinari would you consider to add a virtual container to the tbody to increase performance when rendering long lists without pagination?
I'll take a stab at this if you could just point me in the right direction (react-virtualized?)

Table docs good first issue

Most helpful comment

@oliviertassinari I made it without react-virtualized because it was a mess and it didn't play nice with mui components.
Furthermore, my current solution is a pure-css fixed thead and scrollable tbody with dynamic table height and dynamic columns width (tested on Safari as well).
The always-on-top thead with a scrollable tbody is a must for data grids.

I'm adding scroll debouncing, tweaking performance and fixing some glitch but it's working great: I'm "virtually" rendering about ~2400rows, sooo smooth.

out

Questions on Table components:

  1. how to use ref with MUI components? I'm auto-computing TableThead height: problem is that
<TableHead ref={ ref => this.tableThead = ref }>

is not returning the actual DOM container element of TableThead but the React instance (native behaviour of React when applying ref on a not-DOM element) because that's something that should be applied in the TableThead component itself; other components may need this, so why not having a refHandler on every MUI component which applies the given callback in the first rendered element? (I hope I managed to explain this out @_@)
My current solution is a weird

const theadHeight = ReactDOM.findDOMNode(this.tableThead).clientHeight; // eslint-disable-line react/no-find-dom-node

which means using the deprecated findDomNode and disabling eslint as well.

  1. why is border-bottom applied on cells instead of rows?

  2. what about having a stripe={true} prop to automatically stripe TableBody rows?

  3. why does .MuiIconButton.root have a zIndex style property? 🤔

I hope to publish something stable about this soon so you can give a look it that's something which can be adopted in a general way.

Sorry to bother this much but I'm heavily using mui@next so I'm spotting a lot of use-cases!

All 55 comments

We hope that the current API allow simple integration with react-virtualized. We are open to do any change to the implementation in order to make the integration simpler.

Having an demo example in the docs would be awesome.

If that can help, here is an integration example with the List/Menu https://codesandbox.io/s/oQ0nkl5pk.

@oliviertassinari I made it without react-virtualized because it was a mess and it didn't play nice with mui components.
Furthermore, my current solution is a pure-css fixed thead and scrollable tbody with dynamic table height and dynamic columns width (tested on Safari as well).
The always-on-top thead with a scrollable tbody is a must for data grids.

I'm adding scroll debouncing, tweaking performance and fixing some glitch but it's working great: I'm "virtually" rendering about ~2400rows, sooo smooth.

out

Questions on Table components:

  1. how to use ref with MUI components? I'm auto-computing TableThead height: problem is that
<TableHead ref={ ref => this.tableThead = ref }>

is not returning the actual DOM container element of TableThead but the React instance (native behaviour of React when applying ref on a not-DOM element) because that's something that should be applied in the TableThead component itself; other components may need this, so why not having a refHandler on every MUI component which applies the given callback in the first rendered element? (I hope I managed to explain this out @_@)
My current solution is a weird

const theadHeight = ReactDOM.findDOMNode(this.tableThead).clientHeight; // eslint-disable-line react/no-find-dom-node

which means using the deprecated findDomNode and disabling eslint as well.

  1. why is border-bottom applied on cells instead of rows?

  2. what about having a stripe={true} prop to automatically stripe TableBody rows?

  3. why does .MuiIconButton.root have a zIndex style property? 🤔

I hope to publish something stable about this soon so you can give a look it that's something which can be adopted in a general way.

Sorry to bother this much but I'm heavily using mui@next so I'm spotting a lot of use-cases!

I made it without react-virtualized because it was a mess and it didn't play nice with mui components.

Oh, that's a shame on us. Any idea what we could do to make is possible?

  1. You can try the innerRef to get a ref on the TableHead. ref will get you a ref on the withStyles(TableHead) component. If that doesn't work we could expose a rootRef property. That's a pattern we are already using.

  2. No clue

  3. What do you mean by stripe?
  4. I have removed it in #7467

~~
Oh, that's a shame on us. Any idea what we could do to make is possible?
~
~
Actually is a mix of react-virtualized own containers, native table/thead/tbody DOM elements and the need of having a scrollable tbody which caused the problem.
Too much work was needed with it: simply making an ad-hoc solution for MuiTable was a breeze.

  1. exposing a rootRef would be great (assuming TableThead main element in the render method is the Thead DOM element itself)

  2. should border-bottom be moved to tr then? Should I move this into a dedicated issue?

  3. I mean striped rows: easy to achieve with like tr:nth-child(odd) { backgroundColor: f2f2f2 but since we already have the handy hover={true} coloring why not having the an handy striped={true}? :)

  4. great!

  1. 👍 Sounds like a plan, here is an example, a PR is welcomed.
  2. Bootstrap is doing the same, I think that it's providing more flexibility by using it on the TableCell.
  3. Because it's not in the specification and it can be simply implemented on userland?
  4. 👍
  1. I'm not sure about simply having ref={rootRef} on every component.
    Devs who are using it are probably (mostly) in need to have control over the DOM.
    In your example <FormControl ref={rootRef} ...> will again return a reference to the React component (because FormControl is not a DOM element) and will have the developer use the workaround I had to use.
    Maybe we should have the rootRef behave like the following:

Is the container of the rendered component a DOM element?

  • if yes then apply ref={rootRef} on it
  • otherwise apply rootRef={rootRef} on it => which is going to propagate down to the first DOM element: this way coder will always have the real DOM reference

The catch-all solution I can think about is having muiRef and rootRef properties where the former applies the callback to the "mui component" container (if any) and the latter applies the callback as I said before.
This way Developer can easily have both/either MUI ref and DOM ref (whatever he needs, whenever he needs).
Do you think this make sense?

  1. really? @_@

  2. ops! I always forget about the material specs

  1. Oh, I haven't thought about that case. The issue you are referring to is the following:
  2. Component A takes a ref and a rootRef property
  3. React intercept ref and apply it on the element
  4. Component B receive a rootRef property and apply it as a ref on the root Component C
  5. React intercept this ref and apply it on the element

But what if Component C also have a rootRef property?
Component A can be on user-land, Component B can be the TextField and Component C can be the FormControl.

So my answer, at first we introduced rootRef as a simple helper. We haven't though about industrialization. So, I think that the current behavior is great as deterministic, simple and without the limitation you are describing, if people are interested in accessing the underlying dom element, they can use the findDOMNode API.

@damianobarbati Was react-virtual-list the experiment with Table and virtualization mentioned here?

I'm guessing listComponent = TableBody, itemComponent = TableRow?

I've started this project that uses react-virtualized with Material 1.0 you might be interested in.

https://github.com/techniq/mui-table

It's still a work in progress (see https://github.com/techniq/mui-table/blob/master/TODO.md) but might be useful to you. Docs are lacking but take a look at the stories to get a feel for how to use it.

@techniq This looks promising 😄 ! I think it would be great to revamp the table documentation section at some point to have:
- https://github.com/DevExpress/devextreme-reactive~~ (commercial license)

We can also add it to the https://material-ui-next.com/discover-more/related-projects/#material-ui-specific-projects section.
So, let us know when the project is more mature. I think that we can accept a pull request to promote the project :).

I have done a PR for react-virtual-list here https://github.com/clauderic/react-tiny-virtual-list/pull/30 which allows its usage with material-ui. I struggled to get react-virtual to work and create valid HTML as it has div's hard coded.

cc: @kgregory

@eyn nice work! I just finished up an implementation of an infinite-scrolling table using react-virtualized and material-ui. I hope to throw a gist together soon. When I do I will link it here for any future visitors of this issue and possibly as an example in the docs.

@kgregory did you finished your gist? I'm still looking for a basic solution for virtualized Material UI table and it's not easy to find... It would be a great help!

Thanks for your work!

@neofox No, sorry. I never got around to it, but your reply is a nice reminder. I will try to find time to put something together.

@oliviertassinari your example in one of the first comments that supposedly shows how to use the material list with the react virtualized list is not working. Do you still have that example laying around?

@rolandjitsu Sorry, I don't any examples. It's what this issue is about :).

@oliviertassinari got it. I thought this issue was about the table and not the <List>, or it also covers the lists?

@rolandjitsu The list, the table, the paper, the grid list. It's all the same. This issue is about adding a simple demo so people can see the pattern and apply it to different components. The table is a component you likely gonna need virtualization.

@oliviertassinari I would like to give this a crack, if you can point me in the right direction. As far as I can tell, we need a basic gist with material-ui@next integrated with react-virtualized?

@hassan-zaheer Yes, I think that a simple demo with react-virtualized at the end of the tables docs page will do the trick :).

Hi. Happen to stumble upon this thread when researching for my project. I would like to know if there's any update here concerning the demo or usage documentation? @kgregory Or can you provide any insight in incorporating the two? It'd be nice to make the table look coherent with style

@zd-project You can take a look at my mui-virtualized-table project.

It used to be mui-table but I decided to fork the 2 and support different use cases (mui-table leverages the DOM directly and lets you use things like position: sticky and <table> layout (row/col spans, auto widths, etc) that were difficult with react-virtualized.

With that said, mui-virtualized-table just added column resizing from a nice PR.

Where are we on this guys?

@techniq, thanks for mui-virtualized-table! It's great! Is there any chance we could get it merged into mui-org/material-ui?

I'd be happy if somebody would open a PR that introduces a demo to the docs. This way we could examine if we can improve the table API for better integration with virtualization strategies.

@mastertinner Why do we need to add this to the core? Virtualization is an optimization strategy. Not everybody renders large data sets so I would not burden everybody's bundle with it.

@mastertinner We could start by referencing the project it in the documentation, for instance here: https://material-ui.com/demos/tables/#advanced-use-cases :)

👍 sounds like a plan

@mastertinner While I still use it (when virtualization is needed), I also use my newer mui-table when I don't need a virtualization, and can leverage the dom (using table, tr, td, or css grid for layout, removing the need for explicit column widths, etc) and enables some advanced patterns like multiple position: sticky headers (like this)

@rogerstorm funded this issue with $30. See it on IssueHunt

Decided to play around with this today, I do not recommend 😭 Here's what I've produced so far:
virtualize

Please excuse the lag, my laptop isn't great 👎 It uses Material-UI Components and react-virtualized components

@joshwooding I've done exact same thing what you've done, virtualized + fixed header(with scrollbar on body only). If your implementation uses two seperate <Table>s, then I think you got the same problems I've found.

  • material's alignment props (e.g. numeric) doesn't work, because you might use a component like <div> for layout inside <TableCell>.
  • You might give css properties for td and tr to work virtualized correctly. There's no way to give height or max-height to td and tr and material's <TableCell> has its own padding, margin things so it makes it hard to fix row's height.

Well, the point is a component inside <TableCell> is inevitable. I wonder if you solved this problem in a elegant way. If not, I think virtualized + fixed header(with scrollbar on body only) is hard to be a small(as small as its function),well defined component.

@worudso Currently it uses only one <Table> I need to find a way to pass numeric down but I might be able to get that working. It’s definitely not a small Component and my code’s still a bit all over the place but I’m going to work on it some more and see if I can refine it

I've created a sandbox that demonstrates using material-ui and react-virtualized to produce a virtualized table. This particular example uses react-virtualized to size the Table to the height of the window.

The MuiVirtualizedTable component from the example is also available in this gist.

MuiVirtualizedTable can accept any prop supported by a Table. The rowClassName is not directly applied, but is instead applied in addition to other styles defined in the MuiVirtualizedTable.

The columns prop expects an array of objects that is used when rendering cells for each column. Each object can accept any prop supported by Column.

My apologies for taking so long. I've been busy.

FYI, there's now also react-window which is supposed to be react-virtualized successor. In case anyone wanted to try that one out.

@kgregory Thank you for sharing the codesandbox.

@joshwooding Great demo. It's great to have a fixed header :). I hope you will be able to complete the implementation! Regarding the numeric forwarding, what's preventing you from applying directly on the cells? react-window vs react-virtualized

@oliviertassinari I've set myself the goal of using the Table component's API and right now it doesn't look like you can pass custom props via the Column component. There are probably ways to do it without using the react-virtualized way though

@oliviertassinari I've tried using HeaderRowRenderer but it looks like you can't pass custom props through it without manipulating the existing props since this is what is called to create the columns to pass through to the headerRenderer:

const renderedHeader = headerRenderer({ columnData, dataKey, disableSort, label, sortBy, sortDirection, });

@oliviertassinari For a fixed height table you can just edit @kgregory's demo slightly: https://codesandbox.io/s/6vo8y0929k

@joshwooding Looks great! I can't think of anything missing to make it an official demo.

@issuehuntfest has funded $60.00 to this issue. See it on IssueHunt

@oliviertassinari I'll work on implementing this :)

@joshwooding has submitted a pull request. See it on IssueHunt

@oliviertassinari has rewarded $81.00 to @joshwooding. See it on IssueHunt

  • :moneybag: Total deposit: $90.00
  • :tada: Repository reward(0%): $0.00
  • :wrench: Service fee(10%): $9.00

@oliviertassinari has rewarded $81.00 to @joshwooding. See it on IssueHunt

Well deserved. Nice work @joshwooding!

I have a similar need but for the List component...would the same approach work for making the list work?

@mdizzy Yes, we could add a similar demo for the list.

I am trying right now to make a list virtual with react-window (as the creator suggested to use it in place of virtualized if you don't need the full API, but in the end the API for a list doesn't really change that much)

So far I have two big issues:
1)List items style is broken, especially for stuff like right icons/actions. I guess that the the virtual list injected style doesn't play nicely with the styling from material-ui
2) Sticky subheaders get really messy as well, probably for the same reason (react-window use position absolute to layout the items)

I am going to probably try react-virtualized to see if they work better together

@MastroLindus I assume you have tried our virtualization demo first?

@oliviertassinari I started on my own way, found the problems I described above, googled for it, found this thread.

Then I discovered about the virtualization demo, I checked its code and found out that is basically the same thing I am doing myself (only applied to Tables instead of lists).

My issues are a bit more lists related. The general concept work, but stuff like icons, actions, sticky headers are not working properly.
In detail, referring to my 2 points in the above comment:
Issue number 1 could be an issue with react-window that is not going to be present with react-virtualized. I am going to let you know as soon as I try with it today or tomorrow. Maybe we are lucky and it will just work with react-virtualized. Let's see.
Issue number 2 (sticky subheaders not working properly) I think it's something that will need an ad-hoc solution anyway, as their positioning will probably create a conflict no matter what. Somebody built https://github.com/marchaos/react-virtualized-sticky-tree for a similar problem (having react-virtualized deal with sticky headers) but I didn't have the time to look into it right now

@MastroLindus What issues do we have with https://next.material-ui.com/demos/tables/#virtualized-table?

@oliviertassinari None.
I specified that my issues are specific to virtual lists, not virtual tables. (Again: I am sorry I am writing on the issue for Table Virtualized, there's none for virtualized Lists and I was following up the comment of @mdizzy )

Regarding the lists: using ListItemSecondaryAction, ListItemIcon and ListSubheader (with sticky headers) doesn't seem to work, as their positioning gets messed up.
Without those components, I got a basic list to work with react-window (and I guess would do the same in react-virtualized)

@MastroLindus I think that you can move the discussion to #15149.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

ghost picture ghost  ·  3Comments

revskill10 picture revskill10  ·  3Comments

rbozan picture rbozan  ·  3Comments

iamzhouyi picture iamzhouyi  ·  3Comments

zabojad picture zabojad  ·  3Comments