From ec66744ca36ec6d4b33d5c185d855577d86655c3 Mon Sep 17 00:00:00 2001 From: Alex Andreev Date: Wed, 23 Mar 2022 14:41:58 +0300 Subject: [PATCH] Use react.memo for table rows Signed-off-by: Alex Andreev --- .../components/item-object-list/content.tsx | 78 +++++++------------ .../components/item-object-list/row.tsx | 69 ++++++++++++++++ src/renderer/components/table/table.tsx | 2 +- .../components/virtual-list/virtual-list.tsx | 4 +- 4 files changed, 100 insertions(+), 53 deletions(-) create mode 100644 src/renderer/components/item-object-list/row.tsx diff --git a/src/renderer/components/item-object-list/content.tsx b/src/renderer/components/item-object-list/content.tsx index eb7b5dedb2..540e535065 100644 --- a/src/renderer/components/item-object-list/content.tsx +++ b/src/renderer/components/item-object-list/content.tsx @@ -9,8 +9,8 @@ import React, { ReactNode } from "react"; import { computed, makeObservable } from "mobx"; import { Observer, observer } from "mobx-react"; import { ConfirmDialog, ConfirmDialogParams } from "../confirm-dialog"; -import { Table, TableCell, TableCellProps, TableHead, TableProps, TableRow, TableRowProps, TableSortCallbacks } from "../table"; -import { boundMethod, cssNames, IClassName, isReactNode, prevDefault, stopPropagation } from "../../utils"; +import { Table, TableCell, TableCellProps, TableHead, TableProps, TableRowProps, TableSortCallbacks } from "../table"; +import { boundMethod, cssNames, IClassName, prevDefault, stopPropagation } from "../../utils"; import { AddRemoveButtons, AddRemoveButtonsProps } from "../add-remove-buttons"; import { NoItems } from "../no-items"; import { Spinner } from "../spinner"; @@ -21,6 +21,7 @@ import { MenuActions } from "../menu/menu-actions"; import { MenuItem } from "../menu"; import { Checkbox } from "../checkbox"; import { UserStore } from "../../../common/user-store"; +import { Row } from "./row"; export interface ItemListLayoutContentProps { getFilters: () => Filter[]; @@ -75,66 +76,43 @@ export class ItemListLayoutContent extends React.Component getTableRow(item: I) { const { - isSelectable, renderTableHeader, renderTableContents, renderItemMenu, + renderTableHeader, renderTableContents, renderItemMenu, store, hasDetailsView, onDetails, - copyClassNameFromHeadCells, customizeTableRowProps, detailsItem, + customizeTableRowProps, detailsItem, isSelectable, } = this.props; const { isSelected } = store; + const selected = detailsItem && detailsItem.getId() === item.getId(); + const onCheckboxChange = isSelectable ? prevDefault(() => store.toggleSelection(item)) : null; + const itemMenu = renderItemMenu && ( + +
+ {renderItemMenu(item, store)} +
+
+ ); return ( - onDetails(item)) : undefined} + item={item} + showColumn={this.showColumn} {...customizeTableRowProps(item)} - > - {isSelectable && ( - store.toggleSelection(item))} - /> - )} - {renderTableContents(item).map((content, index) => { - const cellProps: TableCellProps = isReactNode(content) - ? { children: content } - : content; - const headCell = renderTableHeader?.[index]; - - if (copyClassNameFromHeadCells && headCell) { - cellProps.className = cssNames( - cellProps.className, - headCell.className, - ); - } - - if (!headCell || this.showColumn(headCell)) { - return ; - } - - return null; - })} - {renderItemMenu && ( - -
- {renderItemMenu(item, store)} -
-
- )} -
+ /> ); } @boundMethod - getRow(uid: string) { + getRow(uid: string, item: I) { return (
{() => { - const item = this.props.getItems().find(item => item.getId() === uid); - if (!item) return null; return this.getTableRow(item); @@ -202,7 +180,7 @@ export class ItemListLayoutContent extends React.Component return null; } - return this.props.getItems().map(item => this.getRow(item.getId())); + return this.props.getItems().map(item => this.getRow(item.getId(), item)); } renderTableHeader() { @@ -285,11 +263,11 @@ export class ItemListLayoutContent extends React.Component ); } - showColumn({ id: columnId, showWithColumn }: TableCellProps): boolean { + showColumn = ({ id: columnId, showWithColumn }: TableCellProps): boolean => { const { tableId, isConfigurable } = this.props; return !isConfigurable || !UserStore.getInstance().isTableColumnHidden(tableId, columnId, showWithColumn); - } + }; renderColumnVisibilityMenu() { const { renderTableHeader, tableId } = this.props; diff --git a/src/renderer/components/item-object-list/row.tsx b/src/renderer/components/item-object-list/row.tsx new file mode 100644 index 0000000000..5fad6bef3d --- /dev/null +++ b/src/renderer/components/item-object-list/row.tsx @@ -0,0 +1,69 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import React, { memo, ReactNode } from "react"; + +import { cssNames, isReactNode } from "../../utils"; +import { TableCell, TableCellProps, TableRow } from "../table"; + +import type { ItemObject } from "../../../common/item.store"; + +interface RowProps { + selected: boolean; + checked?: boolean; + renderTableHeader?: TableCellProps[]; + cells: (item: I) => (ReactNode | TableCellProps)[]; + itemMenu?: ReactNode; + onCheckboxChange?: (evt: any) => void; + onClick?: (evt: React.MouseEvent) => void; + item: I; + showColumn?: ({ id: columnId, showWithColumn }: TableCellProps) => boolean; +} + +export const Row = memo(({ + selected, + checked, + renderTableHeader, + cells, + itemMenu, + onCheckboxChange, + onClick, + item, + showColumn, + ...rest +}: RowProps) => { + return ( + + {onCheckboxChange && ( + + )} + {cells(item).map((content, index) => { + const cellProps: TableCellProps = isReactNode(content) ? { children: content } : content; + const headCell = renderTableHeader?.[index]; + + if (headCell) { + cellProps.className = cssNames(cellProps.className, headCell.className); + } + + if (!headCell || showColumn(headCell)) { + return ; + } + + return null; + })} + {itemMenu} + + ); +}); diff --git a/src/renderer/components/table/table.tsx b/src/renderer/components/table/table.tsx index 3190c96d33..f702472ca7 100644 --- a/src/renderer/components/table/table.tsx +++ b/src/renderer/components/table/table.tsx @@ -67,7 +67,7 @@ export interface TableProps extends React.DOMAttributes { */ rowLineHeight?: number; customRowHeights?: (item: Item, lineHeight: number, paddings: number) => number; - getTableRow?: (uid: string) => React.ReactElement; + getTableRow?: (uid: string, item?: Item) => React.ReactElement; renderRow?: (item: Item) => React.ReactElement; } diff --git a/src/renderer/components/virtual-list/virtual-list.tsx b/src/renderer/components/virtual-list/virtual-list.tsx index 8e8de118c2..bead115ea8 100644 --- a/src/renderer/components/virtual-list/virtual-list.tsx +++ b/src/renderer/components/virtual-list/virtual-list.tsx @@ -134,7 +134,7 @@ export class VirtualList extends Component { interface RowData { items: ItemObject[]; - getRow?: (uid: string | number) => React.ReactElement; + getRow?: (uid: string | number, item?: ItemObject) => React.ReactElement; } export interface RowProps extends ListChildComponentProps { @@ -146,7 +146,7 @@ const Row = observer((props: RowProps) => { const { items, getRow } = data; const item = items[index]; const uid = typeof item == "string" ? index : items[index].getId(); - const row = getRow(uid); + const row = getRow(uid, item); if (!row) return null;