Skip to content

Commit

Permalink
Added parameter memoizeRows to improve render performance. (#325)
Browse files Browse the repository at this point in the history
  • Loading branch information
SvenReissig committed Jun 4, 2024
1 parent fbd12d6 commit 9a0c1a9
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 45 deletions.
13 changes: 13 additions & 0 deletions .changeset/odd-shirts-invite.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
"@open-pioneer/result-list": minor
---

Added a new optional parameter `memoizeRows` to improve render performance.
When rows are memoized, they are only rerendered under certain conditions (e.g. selection changes, sort order changes), which can
greatly improve performance in some circumstances.

Example:

```tsx
<ResultList mapId={mapId} input={input} memoizeRows={true} />
```
72 changes: 40 additions & 32 deletions src/packages/result-list/DataTable/DataTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import {
} from "@tanstack/react-table";
import classNames from "classnames";
import { useIntl } from "open-pioneer:react-hooks";
import React, { useEffect, useMemo, useState } from "react";
import React, { ReactNode, useEffect, useMemo, useState } from "react";
import { ColumnResizer } from "./ColumnResizer";
import { ColumnSortIndicator } from "./ColumnSortIndicator";
import { useSetupTable } from "./useSetupTable";
Expand All @@ -32,12 +32,14 @@ const LOG = createLogger("result-list:DataTable");
export interface DataTableProps<Data extends BaseFeature> {
data: Data[];
columns: ColumnDef<Data>[];
memoizeRows: boolean;
selectionMode: SelectionMode;
onSelectionChange?(event: ResultListSelectionChangeEvent): void;
}

export function DataTable<Data extends BaseFeature>(props: DataTableProps<Data>) {
const intl = useIntl();
const { memoizeRows } = props;
const { table } = useSetupTable(props);
const isResizing = !!table.getState().columnSizingInfo.isResizingColumn;
const columnSizeVars = useColumnSizeVars(table);
Expand Down Expand Up @@ -77,7 +79,13 @@ export function DataTable<Data extends BaseFeature>(props: DataTableProps<Data>)
<TableHeaderGroup key={headerGroup.id} headerGroup={headerGroup} />
))}
</Thead>
{isResizing ? <MemoizedTableBody table={table} /> : <TableBody table={table} />}
<Tbody className="result-list-table-body">
{memoizeRows || isResizing ? (
<MemoizedTableRows table={table} />
) : (
<TableRows table={table} />
)}
</Tbody>
</Table>
);
}
Expand Down Expand Up @@ -141,38 +149,38 @@ function TableHeader<Data>(props: {
);
}

//un-memoized normal table body component - see memoized version below
function TableBody<Data extends object>({ table }: { table: TanstackTable<Data> }) {
return (
<Tbody className="result-list-table-body">
{table.getRowModel().rows.map((row) => {
return (
<Tr key={row.id} className="result-list-table-row">
{row.getVisibleCells().map((cell) => {
const width = `calc(var(--header-${cell.column.id}-size) * 1px)`;
return (
<Td
key={cell.id}
style={{ width: width }}
className="result-list-table-row"
>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</Td>
);
})}
</Tr>
);
})}
</Tbody>
);
function TableRows<Data extends object>({ table }: { table: TanstackTable<Data> }) {
return table.getRowModel().rows.map((row) => {
return (
<Tr key={row.id} className="result-list-table-row">
{row.getVisibleCells().map((cell) => {
const width = `calc(var(--header-${cell.column.id}-size) * 1px)`;
return (
<Td
key={cell.id}
style={{ width: width }}
className="result-list-table-row"
>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</Td>
);
})}
</Tr>
);
});
}

// Special memoized wrapper for our table body that we will use during column resizing.
// See https://tanstack.com/table/v8/docs/framework/react/examples/column-resizing-performant
const MemoizedTableBody = React.memo(
TableBody,
(prev, next) => prev.table.options.data === next.table.options.data
) as typeof TableBody;
/**
* Rows are memoized and only updating on changes due to sorting columns or selecting rows.
* Removed full Memoization on Resizing because it does not have an additional effect.
*/
function MemoizedTableRows<Data extends object>({ table }: { table: TanstackTable<Data> }) {
const memoizedRows = React.useMemo<ReactNode>(() => {
return <TableRows table={table} />;
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [table, table.getSortedRowModel().rows, table.getSelectedRowModel().rows]);
return memoizedRows;
}

/**
* Instead of calling `column.getSize()` on every render for every header
Expand Down
22 changes: 22 additions & 0 deletions src/packages/result-list/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,28 @@ const ownHighlightStyle = {
<ResultList mapId={mapId} input={input} highlightOptions={ownHighlightStyle} />;
```

### Configuring memoization of rows

The optional property `memoizeRows` determines whether result list table rows should be memoized or not.
The default value is `false`.

If memoization is turned on, the result list only rerenders if

- direct properties of the result list table changes,
- a row is selected (or deselected), or
- the sort order is changed,

but the performance of the result list is greatly improved, especially for high row counts.

`memoizeRows` should remain `false` if additional rerender conditions are present.

Example:

```tsx
import { ResultList } from "@open-pioneer/result-list";
<ResultList mapId={mapId} input={input} memoizeRows={true} />;
```

### Selection

Users can select (and deselect) individual features by clicking on the selection control
Expand Down
33 changes: 20 additions & 13 deletions src/packages/result-list/ResultList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -143,32 +143,32 @@ export interface ResultListProps extends CommonComponentProps {
input: ResultListInput;

/**
* This handler is called whenever the user has changed the selected features in the result-list.
* The selection mode used by the result list. Defaults to `"multi"`.
*/
onSelectionChange?: (event: ResultListSelectionChangeEvent) => void;
selectionMode?: SelectionMode;

/**
* Specifies if the map should zoom to features when they are loaded into the result-list. Defaults to true.
* The style used for the selection controls in a row.
* Defaults to `"checkbox"` if `selectionMode` is `"multi"`, or `"radio"` if `selectionMode` is `"single"`.
*
* Note: `"radio"` can not be used together with multi selection.
*/
enableZoom?: boolean;
selectionStyle?: "radio" | "checkbox";

/**
* Should data be highlighted in the map. Default true.
* This handler is called whenever the user has changed the selected features in the result-list.
*/
enableHighlight?: boolean;
onSelectionChange?: (event: ResultListSelectionChangeEvent) => void;

/**
* The selection mode used by the result list. Defaults to `"multi"`.
* Specifies if the map should zoom to features when they are loaded into the result-list. Defaults to true.
*/
selectionMode?: SelectionMode;
enableZoom?: boolean;

/**
* The style used for the selection controls in a row.
* Defaults to `"checkbox"` if `selectionMode` is `"multi"`, or `"radio"` if `selectionMode` is `"single"`.
*
* Note: `"radio"` can not be used together with multi selection.
* Should data be highlighted in the map. Default true.
*/
selectionStyle?: "radio" | "checkbox";
enableHighlight?: boolean;

/**
* Optional styling option
Expand All @@ -179,6 +179,11 @@ export interface ResultListProps extends CommonComponentProps {
* Optional zooming options
*/
zoomOptions?: ZoomOptions;

/**
* Should each row be memoized to improve render performance. Default `false`.
*/
memoizeRows?: boolean;
}

/**
Expand All @@ -190,6 +195,7 @@ export const ResultList: FC<ResultListProps> = (props) => {
const {
mapId,
input: { data, columns, formatOptions },
memoizeRows = false,
onSelectionChange,
enableZoom = true,
zoomOptions,
Expand Down Expand Up @@ -243,6 +249,7 @@ export const ResultList: FC<ResultListProps> = (props) => {
<DataTable
columns={dataTableColumns}
data={data}
memoizeRows={memoizeRows}
selectionMode={selectionMode}
onSelectionChange={onSelectionChange}
/>
Expand Down

0 comments on commit 9a0c1a9

Please sign in to comment.