Yes
Describe the bug
Rows are re-rendered again on operation like checkbox click.
Minimal replication is available at https://codesandbox.io/s/tannerlinsleyreact-table-row-selection-vqe7c.
Steps -
Disclaimer - I may be missing something.
No you're not missing anything. This response honestly deserves a blog post, but I'll have to link to another one for now: https://kentcdodds.com/blog/react-hooks-pitfalls. Specifically read the section on "Pitfall 4: Overthinking performance".
In a nutshell, rerenders are not bad at all. They are what keep your UI up to date. Expensive rerenders however, can become a problem very quickly. Luckily, React Table v7 is built on this exact premise that rerenders are good, but expensive rerenders are bad.
Moral of the story: Only worry about rerenders if they are expensive or degrading performance.
Unfortunately I did have to use memo() on my Row component. It's unbearably slow in development and definitely not snappy on production (default page size is 50 and I can't change it - that is a requirement). I have a prop there called forceUpdate to which I'm passing row.selected. It's ugly, but I guess I don't have any other option aside from maybe using virtualization.
What are your rows/cells rendering? How many columns?
Just to clarify - it's super slow without memo(). With memo it only rerenders the row that got selected so everything is working smoothly.
I have two bigger tables, one of them has 8 columns, 6 of them render text with tooltips (using tippy), one renders custom checkbox and the last one has either 2 buttons or nothing. The other one can technically have unlimited amount of columns, because they're generated dynamically based on data, but realistically it's just 6-8 columns. One has checkbox, second one a single icon, third one text with tooltip and the rest render "progress bars", it renders something like this:
<div className={styles.container}>
<div className={styles.bar}>
<div className={classes} style={style} />
</div>
<div className={styles.value}>
<div className="text-center">{customValue || formatNumber(value)}</div>
</div>
</div>
where style contains backgroundColor, left and width calculated based on value, the rest are classes from a CSS module. It might be because of those progress bars, but honestly there's not much difference in performance between these two tables. It's all built on styled components (i.e. I have a Row component, Cell component etc.), similarly to how the table was built in early alphas (~alpha.6?) - maybe this is the reason? It's also built on divs with flex plugin, not classic tables. Using profiler doesn't really help - it's just the sheer amount of components to render that makes it slow rather than something specific, but I find it difficult to reduce amount of these components. Perhaps the approach you're using in your examples (a single styled component that styles its children) would perform better? I don't know. For now it works well with memo(), but if you have some suggestions on how to style the table to keep it performant, I'm definitely interested.
My guess is that it's not styled components. IMO, it usually comes down to a few things it could be when things are rendering slowly:
I would need more visibility into your code (a watered down codesandbox would be great) to dig into your specific scenario though.
@paolostyle , using memo is not scalable, as you need to puts hacks like this, which may not be possible(buggy/not maintainable) for other features if you are creating a full-fledged grid with all features.
@tannerlinsley , after going through all docs and posts, i understand your point, that re-renders are OK but non-performant re-render are bad.
But in our case of react-table, we can check the given example only https://codesandbox.io/s/tannerlinsleyreact-table-row-selection-vqe7c( https://vqe7c.csb.app/ ) how can we find non-performant components in this case, because the example given is minimal and take a good amount to time to reflect a simple user action.
I am worried about performance, so that the combination of all hooks + good amount of data + custom code(provided by lib's user) would not cause bad UX.
Because currently by using following things, faced latency on user actions :-
useFilters,
useSortBy,
useGroupBy,
useExpanded,
useRowSelect,
useRowClick,
useFlexLayout
What are your thoughts on it ?
My initial thoughts are:
prepareRow. Doing this for hundreds or thousands of rows in a render is going to be expensive. Pagination and / or virtualization of those rows is necessary if that is your use case.It's been said before that moving to React hooks would probably uncover a lot of issues in the way we used to write react. This was mostly referring to the way we handle side effects, but I also believe that it has to do with performance issues as well (older versions of React encouraged render-bailout, React hooks encourages more memoization). There are clearly other components out there in the ecosystem that are not very performant, but the only way to find them is to profile. IMO, the easiest way to profile is by isolating the circumstances into a codesandbox and profiling within that isolated context.
So I went ahead and profiled the sandbox you posted above and here are my findings:
debug: true in React Table and didn't find any peculiarities with the debug statements.rows.slice(0, 20).map(...) in your Table component you'll see the performance jump drastically.To help illustrate this, take a look at this codesandbox.
You'll notice in the console that as you check the boxes, the timing of useTable is very fast. Less than a millisecond in most cases. But rendering the table is very slow, close to half a second. This is a great way to measure if React Table is being slow, or if your renderer is being slow.
Thank you @tannerlinsley for the detailed explanation, this is much helpful. To decrease the rendering time will scratch more.
Awesome. Closing for now. Thanks!
Most helpful comment
My initial thoughts are:
prepareRow. Doing this for hundreds or thousands of rows in a render is going to be expensive. Pagination and / or virtualization of those rows is necessary if that is your use case.It's been said before that moving to React hooks would probably uncover a lot of issues in the way we used to write react. This was mostly referring to the way we handle side effects, but I also believe that it has to do with performance issues as well (older versions of React encouraged render-bailout, React hooks encourages more memoization). There are clearly other components out there in the ecosystem that are not very performant, but the only way to find them is to profile. IMO, the easiest way to profile is by isolating the circumstances into a codesandbox and profiling within that isolated context.
So I went ahead and profiled the sandbox you posted above and here are my findings:
debug: truein React Table and didn't find any peculiarities with the debug statements.rows.slice(0, 20).map(...)in your Table component you'll see the performance jump drastically.