Reputation: 175
I have adapted this example from the material-ui table component example from the docs. In the provided example, rows can be selected, in which case the checkbox for that row will be checked. The problem with the example implementation is that when a change is made to any row, all of the rows will be re-rendered, even though only one of them has changed. This works fine as long as the number of rows is quite small but quickly degrades when rendering more rows.
As an attempted fix for this, I tried to implement shouldComponentUpdate in my Row components, to check if the row data (via the 'name' property), or the isItemSelected prop has changed (I ignored the other props as the callback function props will always be considered to have changed on each render). For some reason, this totally breaks my selected state in the parent Table component.
Here is my example, highlighting the issue: table-bug-example. As is, this reflects the example provided in the docs. You can see the lag issues if you set the rows per page drop down to a higher number. You can uncomment my shouldComponentUpdate implementation in the Row Component, to see how the state breaks in the Table component, when using shouldComponentUpdate in the children.
Expected Flow:
I have been struggling with this for several hours and can't for the life of me figure out what is wrong. I've also tried using React.memo, as well as different implementations of the setSelected state in the table component (I was previously using a dictionary to keep track of the selected rows, but for now I've left the default implementation provided in the material-ui docs, to minimize any issues introduced in my code).
Might anyone happen to know what is going on here?
Upvotes: 0
Views: 222
Reputation: 2267
When you used your custom shouldComponentUpdate what happened you "saved" the old version of handleClick inside of your custom MyRow, and since handleClick is not a pure function(it is using previous selection to determine prev selection state) it was always reading the wrong state.
For example you have 2 rows (row1 and row2). On initial render selection is : [], and the both render with the version of handleClick that has captured selection as []. You click on row1, it will rerender since its isItemSelected prop has changed, but row2 will not since your shouldComponentUpdate returned false. That means when you click on row2 it will use the handleClick from the first render wich had the selection as [] and it will uncheck row1 selection. There is no magic mechanism that binds selection, it all uses javascript context capturing.
Thats why custom shouldComponent is very dangerous. I would recommend using MyRow as a PureComponent (no need to use React.memo, you can just extend PureComponent) and transform handleClick inside table so it doesnt change (doesnt depend on current selection). Something like this: https://codesandbox.io/embed/material-demo-i0yp2?fontsize=14&hidenavigation=1&theme=dark
Hope this helps.
Upvotes: 1