Skip to content

Commit 65c537c

Browse files
Datatable: cell memoization options and documentation (#8035)
* Create cell memoization options in datatable props, including detailed documentation * Prettier * Fixed TS error * Fixed TS error * Update datatable.d.ts --------- Co-authored-by: Melloware <mellowaredev@gmail.com>
1 parent e32fad7 commit 65c537c

File tree

7 files changed

+65
-5
lines changed

7 files changed

+65
-5
lines changed

components/lib/datatable/BodyCell.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -691,14 +691,22 @@ export const RadioCheckCell = React.memo(
691691

692692
RadioCheckCell.displayName = 'RadioCheckCell';
693693

694+
const defaultKeysToCompare = ['rowData', 'field', 'allowCellSelection', 'isCellSelected', 'editMode', 'index', 'tabIndex', 'editing', 'expanded', 'editingMeta', 'frozenCol', 'alignFrozenCol'];
695+
694696
export const BodyCell = React.memo(
695697
(props) => {
696698
return <Cell {...props} />;
697699
},
698700
(prevProps, nextProps) => {
699-
const keysToCompare = ['field', 'allowCellSelection', 'isCellSelected', 'editMode', 'index', 'tabIndex', 'editing', 'expanded', 'editingMeta', 'rowData', 'frozenCol', 'alignFrozenCol'];
701+
if (nextProps.cellMemo === false) return false;
700702

701-
return ObjectUtils.selectiveCompare(prevProps, nextProps, keysToCompare);
703+
const memoProps = nextProps.cellMemoProps;
704+
const keysToCompare = Array.isArray(memoProps) && memoProps.every((prop) => typeof prop === 'string') ? memoProps : defaultKeysToCompare;
705+
706+
const memoPropsDepth = nextProps.cellMemoPropsDepth;
707+
const depth = typeof memoPropsDepth === 'number' && memoPropsDepth > 0 ? memoPropsDepth : 1;
708+
709+
return ObjectUtils.selectiveCompare(prevProps, nextProps, keysToCompare, depth);
702710
}
703711
);
704712

components/lib/datatable/BodyRow.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -605,6 +605,9 @@ export const BodyRow = React.memo((props) => {
605605
const cellProps = mergeProps({
606606
hostName: props.hostName,
607607
allowCellSelection: props.allowCellSelection,
608+
cellMemo: props.cellMemo,
609+
cellMemoProps: props.cellMemoProps,
610+
cellMemoPropsDepth: props.cellMemoPropsDepth,
608611
cellClassName: props.cellClassName,
609612
checkIcon: props.checkIcon,
610613
collapsedRowIcon: props.collapsedRowIcon,

components/lib/datatable/DataTable.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1688,6 +1688,9 @@ export const DataTable = React.forwardRef((inProps, ref) => {
16881688
<TableBody
16891689
hostName="DataTable"
16901690
ref={frozenBodyRef}
1691+
cellMemo={props.cellMemo}
1692+
cellMemoProps={props.cellMemoProps}
1693+
cellMemoPropsDepth={props.cellMemoPropsDepth}
16911694
cellClassName={props.cellClassName}
16921695
cellSelection={props.cellSelection}
16931696
checkIcon={props.checkIcon}
@@ -1773,6 +1776,9 @@ export const DataTable = React.forwardRef((inProps, ref) => {
17731776
<TableBody
17741777
hostName="DataTable"
17751778
ref={bodyRef}
1779+
cellMemo={props.cellMemo}
1780+
cellMemoProps={props.cellMemoProps}
1781+
cellMemoPropsDepth={props.cellMemoPropsDepth}
17761782
cellClassName={props.cellClassName}
17771783
cellSelection={props.cellSelection}
17781784
checkIcon={props.checkIcon}

components/lib/datatable/TableBody.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1006,6 +1006,9 @@ export const TableBody = React.memo(
10061006
hostName={props.hostName}
10071007
allowCellSelection={_allowCellSelection}
10081008
allowRowSelection={_allowRowSelection}
1009+
cellMemo={props.cellMemo}
1010+
cellMemoProps={props.cellMemoProps}
1011+
cellMemoPropsDepth={props.cellMemoPropsDepth}
10091012
cellClassName={props.cellClassName}
10101013
checkIcon={props.checkIcon}
10111014
collapsedRowIcon={props.collapsedRowIcon}

components/lib/datatable/datatable.d.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1101,6 +1101,45 @@ interface DataTableBaseProps<TValue extends DataTableValueArray> extends Omit<Re
11011101
* @defaultValue 960px
11021102
*/
11031103
breakpoint?: string | undefined;
1104+
/**
1105+
* Whether to enable cell memoization.
1106+
*
1107+
* When the memoization is enabled, be sure to:
1108+
* 1- Update the value prop (i.e., row data) to trigger a re-render of the cells of a given row.
1109+
* 2- Where necessary, use the spread operator (...) when updating the value prop objs which creates new fresh
1110+
* objects and avoids mutating the same objects.
1111+
*
1112+
* When the memoization is disabled, a re-render of the datatable will trigger a re-render of all cells, which can
1113+
* lead to performance issues with large datasets and is therefore not recommended.
1114+
* @defaultValue true
1115+
*/
1116+
cellMemo?: boolean;
1117+
/**
1118+
* The cell props to be checked at memoization.
1119+
*
1120+
* Possible cell props are:
1121+
* 'hostName', 'allowCellSelection', 'cellMemo', 'cellMemoProps', 'cellMemoPropsDepth', 'cellClassName', 'checkIcon', 'collapsedRowIcon',
1122+
* 'field', 'resolveFieldData', 'column', 'cProps', 'dataKey', 'editMode', 'editing', 'editingMeta', 'onEditingMetaChange', 'editingKey',
1123+
* 'getEditingRowData', 'expanded', 'expandedRowIcon', 'frozenRow', 'frozenCol', 'alignFrozenCol', 'index', 'isSelectable', 'onCheckboxChange',
1124+
* 'onClick', 'onMouseDown', 'onMouseUp', 'onRadioChange', 'onRowEditCancel', 'onRowEditInit', 'onRowEditSave', 'onRowToggle', 'responsiveLayout',
1125+
* 'rowData', 'rowEditorCancelIcon', 'rowEditorInitIcon', 'rowEditorSaveIcon', 'rowIndex', 'rowSpan', 'selectOnEdit', 'isRowSelected', 'isCellSelected',
1126+
* 'selectionAriaLabel', 'showRowReorderElement', 'showSelectionElement', 'tabIndex', 'getTabIndex', 'tableProps', 'tableSelector', 'value',
1127+
* 'getVirtualScrollerOption', 'ptCallbacks', 'metaData', 'unstyled', 'findNextSelectableCell', 'findPrevSelectableCell', 'findDownSelectableCell',
1128+
* 'findUpSelectableCell', 'focusOnElement', 'focusOnInit', 'updateStickyPosition'
1129+
*
1130+
* IMPORTANT: Including a function to be checked will in general disable the memoization in practice, since functions are
1131+
* compared by reference.
1132+
*
1133+
* @defaultValue ['rowData', 'field', 'allowCellSelection', 'isCellSelected', 'editMode', 'index', 'tabIndex',
1134+
* 'editing', 'expanded', 'editingMeta', 'frozenCol', 'alignFrozenCol']
1135+
*/
1136+
cellMemoProps?: string[];
1137+
/**
1138+
* The comparison depth when checking cell props (e.g., rowData) at memoization.
1139+
*
1140+
* @defaultValue 1
1141+
*/
1142+
cellMemoPropsDepth?: number;
11041143
/**
11051144
* Icon to display in the checkbox.
11061145
*/

components/lib/utils/ObjectUtils.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -635,9 +635,10 @@ export default class ObjectUtils {
635635
* @param {object} a - The first object to compare.
636636
* @param {object} b - The second object to compare.
637637
* @param {string[]} [keysToCompare] - The keys to compare. If not provided, performs a shallow comparison using absoluteCompare.
638+
* @param {number} [maxDepth=1] - The maximum depth to compare if the variables are objects.
638639
* @returns {boolean} True if all specified properties are equal, false otherwise.
639640
*/
640-
static selectiveCompare(a, b, keysToCompare) {
641+
static selectiveCompare(a, b, keysToCompare, maxDepth = 1) {
641642
if (a === b) return true;
642643
if (!a || !b || typeof a !== 'object' || typeof b !== 'object') return false;
643644
if (!keysToCompare) return this.absoluteCompare(a, b, 1); // If no keys are provided, the comparison is limited to one depth level.
@@ -649,7 +650,7 @@ export default class ObjectUtils {
649650
const isObject = typeof aValue === 'object' && aValue !== null && typeof bValue === 'object' && bValue !== null;
650651

651652
// If the current key is an object, they are compared in one further level only.
652-
if (isObject && !this.absoluteCompare(aValue, bValue, 1)) return false;
653+
if (isObject && !this.absoluteCompare(aValue, bValue, maxDepth)) return false;
653654
if (!isObject && aValue !== bValue) return false;
654655
}
655656

components/lib/utils/utils.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ export declare class ObjectUtils {
153153
static sort(value1: any, value2: any, order: number, locale: string | string[]): number;
154154
static getNestedValue(obj: object, path: string): any;
155155
static absoluteCompare(objA: object, objB: object, maxDepth?: number, currentDepth?: number): boolean;
156-
static selectiveCompare(a: object, b: object, keysToCompare?: string[]): boolean;
156+
static selectiveCompare(a: object, b: object, keysToCompare?: string[], maxDepth?: number): boolean;
157157
}
158158

159159
/**

0 commit comments

Comments
 (0)