-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Make it easier to prevent unnecessary DataTable
re-renders
#1357
Conversation
This hook is useful for wrapping callbacks to avoid unnecessary re-renders due to callback props changing. See hypothesis/via#1129.
- Memo context provider values in various components. Otherwise every render of the component containing the `*.Provider` will cause any descendant consumers to re-render, even if the descendant has been memoed. Before this change, JSX like the following would result in a `DataTable` being re-rendered on every `Container` render: ``` function Container() { const table = useMemo(() => <DataTable ... />, [...]); return <Scroll>{table}</Scroll>; } ``` This happened because the `Scroll` component contained a context provider that was consumed by the `DataTable` and thus every render of the `Scroll` would re-render the `DataTable` child. - Memo rendered `DataTable` rows such that they only re-render if one of the following changes: - The row content - The columns - A custom `renderItem` callback - The selected row Users of `DataTable` can then more easily eliminate re-renders of large tables by memoing these props/callbacks.
@@ -49,6 +50,10 @@ export type DataTableProps<Row> = CompositeProps & | |||
ComponentProps<Row> & | |||
Omit<JSX.HTMLAttributes<HTMLElement>, 'size' | 'rows' | 'role' | 'loading'>; | |||
|
|||
function defaultRenderItem<Row>(r: Row, field: keyof Row): ComponentChildren { | |||
return r[field] as ComponentChildren; | |||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hoisted this default prop value to a top-level function so it doesn't change on each render.
onConfirmRow?.(row); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wrap callback props so we don't unnecessarily re-render even if consumers fail to useCallback
these.
@@ -0,0 +1,39 @@ | |||
// These tests use Preact directly, rather than via Enzyme, to simplify debugging. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This file and use-stable-callback.ts
were copied from Via.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good one 👍🏼
This avoids an unnecessary re-render of the DataTable if the chapters haven't changed, which is important if the book has a lot of pages or chapters. For this change to work requires hypothesis/frontend-shared#1357.
This avoids an unnecessary re-render of the DataTable if the chapters haven't changed, which is important if the book has a lot of pages or chapters. For this change to work requires hypothesis/frontend-shared#1357.
…frontend-shared This hook was copied from Via into the frontend-shared package in hypothesis/frontend-shared#1357.
This was an oversight from #1357.
This was an oversight from #1357.
…frontend-shared This hook was copied from Via into the frontend-shared package in hypothesis/frontend-shared#1357.
…frontend-shared This hook was copied from Via into the frontend-shared package in hypothesis/frontend-shared#1357.
…frontend-shared This hook was copied from Via into the frontend-shared package in hypothesis/frontend-shared#1357.
While adding some new UI to the VitalSource chapter/page selection dialog (see hypothesis/lms#5808) I found that the UI got very slow when working with a book that had a large (> 1000) number of entries in the chapter list. Such a large chapter list could really use virtualization to limit rendering time, but memo-ing is much easier to add and still has a significant impact.
I tried memo-ing the
DataTable
but that initially had no effect, due to the parentScroll
triggering a re-render even if theDataTable
was memoed. This PR fixes that issue, and adds a bunch of additional memo-ing insideDataTable
to make it easier for downstream apps to prevent re-renders. They now need to just memo: 1) The rows, 2) the columns, 3) the selected item. For better results they can also memo the wholeDataTable
.In the process I imported the useStableCallback hook from Via since it is generally useful for these kinds of optimizations and I expect it will be useful in other applications in future.