Skip to content
This repository was archived by the owner on Jun 28, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions components/lib/datatable/BodyCell.js
Original file line number Diff line number Diff line change
Expand Up @@ -691,14 +691,22 @@ export const RadioCheckCell = React.memo(

RadioCheckCell.displayName = 'RadioCheckCell';

const defaultKeysToCompare = ['rowData', 'field', 'allowCellSelection', 'isCellSelected', 'editMode', 'index', 'tabIndex', 'editing', 'expanded', 'editingMeta', 'frozenCol', 'alignFrozenCol'];

export const BodyCell = React.memo(
(props) => {
return <Cell {...props} />;
},
(prevProps, nextProps) => {
const keysToCompare = ['field', 'allowCellSelection', 'isCellSelected', 'editMode', 'index', 'tabIndex', 'editing', 'expanded', 'editingMeta', 'rowData', 'frozenCol', 'alignFrozenCol'];
if (nextProps.cellMemo === false) return false;

return ObjectUtils.selectiveCompare(prevProps, nextProps, keysToCompare);
const memoProps = nextProps.cellMemoProps;
const keysToCompare = Array.isArray(memoProps) && memoProps.every((prop) => typeof prop === 'string') ? memoProps : defaultKeysToCompare;

const memoPropsDepth = nextProps.cellMemoPropsDepth;
const depth = typeof memoPropsDepth === 'number' && memoPropsDepth > 0 ? memoPropsDepth : 1;

return ObjectUtils.selectiveCompare(prevProps, nextProps, keysToCompare, depth);
}
);

Expand Down
3 changes: 3 additions & 0 deletions components/lib/datatable/BodyRow.js
Original file line number Diff line number Diff line change
Expand Up @@ -605,6 +605,9 @@ export const BodyRow = React.memo((props) => {
const cellProps = mergeProps({
hostName: props.hostName,
allowCellSelection: props.allowCellSelection,
cellMemo: props.cellMemo,
cellMemoProps: props.cellMemoProps,
cellMemoPropsDepth: props.cellMemoPropsDepth,
cellClassName: props.cellClassName,
checkIcon: props.checkIcon,
collapsedRowIcon: props.collapsedRowIcon,
Expand Down
6 changes: 6 additions & 0 deletions components/lib/datatable/DataTable.js
Original file line number Diff line number Diff line change
Expand Up @@ -1688,6 +1688,9 @@ export const DataTable = React.forwardRef((inProps, ref) => {
<TableBody
hostName="DataTable"
ref={frozenBodyRef}
cellMemo={props.cellMemo}
cellMemoProps={props.cellMemoProps}
cellMemoPropsDepth={props.cellMemoPropsDepth}
cellClassName={props.cellClassName}
cellSelection={props.cellSelection}
checkIcon={props.checkIcon}
Expand Down Expand Up @@ -1773,6 +1776,9 @@ export const DataTable = React.forwardRef((inProps, ref) => {
<TableBody
hostName="DataTable"
ref={bodyRef}
cellMemo={props.cellMemo}
cellMemoProps={props.cellMemoProps}
cellMemoPropsDepth={props.cellMemoPropsDepth}
cellClassName={props.cellClassName}
cellSelection={props.cellSelection}
checkIcon={props.checkIcon}
Expand Down
3 changes: 3 additions & 0 deletions components/lib/datatable/TableBody.js
Original file line number Diff line number Diff line change
Expand Up @@ -1006,6 +1006,9 @@ export const TableBody = React.memo(
hostName={props.hostName}
allowCellSelection={_allowCellSelection}
allowRowSelection={_allowRowSelection}
cellMemo={props.cellMemo}
cellMemoProps={props.cellMemoProps}
cellMemoPropsDepth={props.cellMemoPropsDepth}
cellClassName={props.cellClassName}
checkIcon={props.checkIcon}
collapsedRowIcon={props.collapsedRowIcon}
Expand Down
39 changes: 39 additions & 0 deletions components/lib/datatable/datatable.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1101,6 +1101,45 @@ interface DataTableBaseProps<TValue extends DataTableValueArray> extends Omit<Re
* @defaultValue 960px
*/
breakpoint?: string | undefined;
/**
* Whether to enable cell memoization.
*
* When the memoization is enabled, be sure to:
* 1- Update the value prop (i.e., row data) to trigger a re-render of the cells of a given row.
* 2- Where necessary, use the spread operator (...) when updating the value prop objs which creates new fresh
* objects and avoids mutating the same objects.
*
* When the memoization is disabled, a re-render of the datatable will trigger a re-render of all cells, which can
* lead to performance issues with large datasets and is therefore not recommended.
* @defaultValue true
*/
cellMemo?: boolean;
/**
* The cell props to be checked at memoization.
*
* Possible cell props are:
* 'hostName', 'allowCellSelection', 'cellMemo', 'cellMemoProps', 'cellMemoPropsDepth', 'cellClassName', 'checkIcon', 'collapsedRowIcon',
* 'field', 'resolveFieldData', 'column', 'cProps', 'dataKey', 'editMode', 'editing', 'editingMeta', 'onEditingMetaChange', 'editingKey',
* 'getEditingRowData', 'expanded', 'expandedRowIcon', 'frozenRow', 'frozenCol', 'alignFrozenCol', 'index', 'isSelectable', 'onCheckboxChange',
* 'onClick', 'onMouseDown', 'onMouseUp', 'onRadioChange', 'onRowEditCancel', 'onRowEditInit', 'onRowEditSave', 'onRowToggle', 'responsiveLayout',
* 'rowData', 'rowEditorCancelIcon', 'rowEditorInitIcon', 'rowEditorSaveIcon', 'rowIndex', 'rowSpan', 'selectOnEdit', 'isRowSelected', 'isCellSelected',
* 'selectionAriaLabel', 'showRowReorderElement', 'showSelectionElement', 'tabIndex', 'getTabIndex', 'tableProps', 'tableSelector', 'value',
* 'getVirtualScrollerOption', 'ptCallbacks', 'metaData', 'unstyled', 'findNextSelectableCell', 'findPrevSelectableCell', 'findDownSelectableCell',
* 'findUpSelectableCell', 'focusOnElement', 'focusOnInit', 'updateStickyPosition'
*
* IMPORTANT: Including a function to be checked will in general disable the memoization in practice, since functions are
* compared by reference.
*
* @defaultValue ['rowData', 'field', 'allowCellSelection', 'isCellSelected', 'editMode', 'index', 'tabIndex',
* 'editing', 'expanded', 'editingMeta', 'frozenCol', 'alignFrozenCol']
*/
cellMemoProps?: string[];
/**
* The comparison depth when checking cell props (e.g., rowData) at memoization.
*
* @defaultValue 1
*/
cellMemoPropsDepth?: number;
/**
* Icon to display in the checkbox.
*/
Expand Down
5 changes: 3 additions & 2 deletions components/lib/utils/ObjectUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -635,9 +635,10 @@ export default class ObjectUtils {
* @param {object} a - The first object to compare.
* @param {object} b - The second object to compare.
* @param {string[]} [keysToCompare] - The keys to compare. If not provided, performs a shallow comparison using absoluteCompare.
* @param {number} [maxDepth=1] - The maximum depth to compare if the variables are objects.
* @returns {boolean} True if all specified properties are equal, false otherwise.
*/
static selectiveCompare(a, b, keysToCompare) {
static selectiveCompare(a, b, keysToCompare, maxDepth = 1) {
if (a === b) return true;
if (!a || !b || typeof a !== 'object' || typeof b !== 'object') return false;
if (!keysToCompare) return this.absoluteCompare(a, b, 1); // If no keys are provided, the comparison is limited to one depth level.
Expand All @@ -649,7 +650,7 @@ export default class ObjectUtils {
const isObject = typeof aValue === 'object' && aValue !== null && typeof bValue === 'object' && bValue !== null;

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

Expand Down
2 changes: 1 addition & 1 deletion components/lib/utils/utils.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ export declare class ObjectUtils {
static sort(value1: any, value2: any, order: number, locale: string | string[]): number;
static getNestedValue(obj: object, path: string): any;
static absoluteCompare(objA: object, objB: object, maxDepth?: number, currentDepth?: number): boolean;
static selectiveCompare(a: object, b: object, keysToCompare?: string[]): boolean;
static selectiveCompare(a: object, b: object, keysToCompare?: string[], maxDepth?: number): boolean;
}

/**
Expand Down
Loading