Prevent unnecessary re-rendering of table rows

This commit is contained in:
Oliver Walters 2026-06-27 08:28:05 +00:00
parent a100623c44
commit 1dfcfaa6c3
2 changed files with 44 additions and 13 deletions

View File

@ -15,20 +15,12 @@ import { StandaloneField } from '../StandaloneField';
export interface TableFieldRowProps {
item: any;
idx: number;
rowId?: string | number;
rowId: string | number;
rowErrors: any;
changeFn: (idx: number | string, key: string, value: any) => void;
removeFn: (idx: number | string) => void;
}
function getRowIdentifier(item: any, idx: number): string | number {
if (item && typeof item === 'object') {
return item.pk ?? item.item ?? item.id ?? item.uuid ?? idx;
}
return item ?? idx;
}
function TableFieldRow({
item,
idx,
@ -40,7 +32,7 @@ function TableFieldRow({
}: Readonly<{
item: any;
idx: number;
rowId?: string | number;
rowId: string | number;
errors: any;
definition: ApiFormFieldType;
changeFn: (idx: number, key: string, value: any) => void;
@ -110,6 +102,34 @@ export function TableField({
const valueRef = useRef(value);
const onChangeRef = useRef(field.onChange);
const rowIndexByIdRef = useRef(new Map<string | number, number>());
const generatedRowIdsRef = useRef(new WeakMap<object, string>());
const generatedRowIdCounterRef = useRef(0);
const getRowIdentifier = useCallback(
(item: any, idx: number): string | number => {
if (item && typeof item === 'object') {
const intrinsicId = item.pk ?? item.item ?? item.id ?? item.uuid;
if (intrinsicId !== undefined && intrinsicId !== null) {
return intrinsicId;
}
const existingGeneratedId = generatedRowIdsRef.current.get(item);
if (existingGeneratedId) {
return existingGeneratedId;
}
generatedRowIdCounterRef.current += 1;
const generatedId = `table-row-generated-${generatedRowIdCounterRef.current}`;
generatedRowIdsRef.current.set(item, generatedId);
return generatedId;
}
return item ?? idx;
},
[]
);
// Keep refs in sync with latest values without introducing them as deps
valueRef.current = value;
@ -123,7 +143,7 @@ export function TableField({
});
rowIndexByIdRef.current = nextRowIndexById;
}, [value]);
}, [value, getRowIdentifier]);
const resolveRowIndex = useCallback((identifier: number | string) => {
const mappedIndex = rowIndexByIdRef.current.get(identifier);

View File

@ -634,7 +634,7 @@ function StockOperationsRow({
returnStock?: boolean;
record?: any;
}) {
const rowId = props.rowId ?? props.idx;
const rowId = props.rowId;
useWhyDidYouUpdate(`StockOperationsRow-${rowId}`, props);
@ -847,9 +847,20 @@ function StockOperationsRow({
const MemoizedStockOperationsRow = memo(
StockOperationsRow,
(previousProps, nextProps) => {
const prevItem = previousProps.props.item;
const nextItem = nextProps.props.item;
const itemUnchanged =
prevItem === nextItem ||
(prevItem?.pk === nextItem?.pk &&
prevItem?.quantity === nextItem?.quantity &&
prevItem?.status === nextItem?.status &&
prevItem?.packaging === nextItem?.packaging &&
prevItem?.merge === nextItem?.merge);
return (
previousProps.props.rowId === nextProps.props.rowId &&
previousProps.props.item === nextProps.props.item &&
itemUnchanged &&
previousProps.props.rowErrors === nextProps.props.rowErrors &&
previousProps.props.changeFn === nextProps.props.changeFn &&
previousProps.props.removeFn === nextProps.props.removeFn &&