Skip to content

Commit 94f41fb

Browse files
committed
Stabilise the record table cell context values
Every cell provider built its context value as a fresh object literal on each render, and RecordTableContextProvider rebuilt the whole visibleRecordFields array with new field objects too. With ~240 virtualized rows that is a new recordField identity per cell on any render of the table provider, so memoizing the cell value alone would have been a no-op. Memoize the provider value and the mapped fields first, then the two cell context values that depend on them, and hoist the constant scope value.
1 parent 9ab6988 commit 94f41fb

4 files changed

Lines changed: 67 additions & 34 deletions

File tree

packages/twenty-front/src/modules/object-record/record-table/components/RecordTableContextProvider.tsx

Lines changed: 45 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1-
import { type ReactNode, useCallback } from 'react';
1+
import { type ReactNode, useCallback, useMemo } from 'react';
22

33
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
44
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
5-
import { RecordTableContextProvider as RecordTableContextInternalProvider } from '@/object-record/record-table/contexts/RecordTableContext';
5+
import {
6+
RecordTableContextProvider as RecordTableContextInternalProvider,
7+
type RecordTableContextValue,
8+
} from '@/object-record/record-table/contexts/RecordTableContext';
69

710
import { useObjectPermissionsForObject } from '@/object-record/hooks/useObjectPermissionsForObject';
811
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
@@ -17,6 +20,10 @@ import { useAtomComponentSelectorValue } from '@/ui/utilities/state/jotai/hooks/
1720
import { useIsTouchDevice } from 'twenty-ui/utilities';
1821
import { OpenRecordIn } from 'twenty-shared/types';
1922

23+
const RECORD_FIELDS_SCOPE_CONTEXT_VALUE = {
24+
scopeInstanceId: RECORD_TABLE_CELL_INPUT_ID_PREFIX,
25+
};
26+
2027
type RecordTableContextProviderProps = {
2128
viewBarId: string;
2229
recordTableId: string;
@@ -46,6 +53,15 @@ export const RecordTableContextProvider = ({
4653
visibleRecordFieldsComponentSelector,
4754
);
4855

56+
const visibleRecordFieldsWithMinWidth = useMemo(
57+
() =>
58+
visibleRecordFields.map((field) => ({
59+
...field,
60+
size: Math.max(field.size, RECORD_TABLE_COLUMN_MIN_WIDTH),
61+
})),
62+
[visibleRecordFields],
63+
);
64+
4965
const { updateOneRecord } = useUpdateOneRecord();
5066

5167
const updateRecord = useCallback(
@@ -70,26 +86,34 @@ export const RecordTableContextProvider = ({
7086
? 'CLICK'
7187
: 'MOUSE_DOWN';
7288

89+
const recordTableContextValue: RecordTableContextValue = useMemo(
90+
() => ({
91+
viewBarId,
92+
objectMetadataItem,
93+
objectMetadataItems,
94+
recordTableId,
95+
objectNameSingular,
96+
objectPermissions,
97+
visibleRecordFields: visibleRecordFieldsWithMinWidth,
98+
onRecordIdentifierClick,
99+
triggerEvent,
100+
}),
101+
[
102+
viewBarId,
103+
objectMetadataItem,
104+
objectMetadataItems,
105+
recordTableId,
106+
objectNameSingular,
107+
objectPermissions,
108+
visibleRecordFieldsWithMinWidth,
109+
onRecordIdentifierClick,
110+
triggerEvent,
111+
],
112+
);
113+
73114
return (
74-
<RecordFieldsScopeContextProvider
75-
value={{ scopeInstanceId: RECORD_TABLE_CELL_INPUT_ID_PREFIX }}
76-
>
77-
<RecordTableContextInternalProvider
78-
value={{
79-
viewBarId,
80-
objectMetadataItem,
81-
objectMetadataItems,
82-
recordTableId,
83-
objectNameSingular,
84-
objectPermissions,
85-
visibleRecordFields: visibleRecordFields.map((field) => ({
86-
...field,
87-
size: Math.max(field.size, RECORD_TABLE_COLUMN_MIN_WIDTH),
88-
})),
89-
onRecordIdentifierClick,
90-
triggerEvent,
91-
}}
92-
>
115+
<RecordFieldsScopeContextProvider value={RECORD_FIELDS_SCOPE_CONTEXT_VALUE}>
116+
<RecordTableContextInternalProvider value={recordTableContextValue}>
93117
<RecordTableUpdateContext.Provider value={updateRecord}>
94118
{children}
95119
</RecordTableUpdateContext.Provider>

packages/twenty-front/src/modules/object-record/record-table/contexts/RecordTableContext.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { type RecordField } from '@/object-record/record-field/types/RecordField
33
import { type ObjectPermission } from '~/generated-metadata/graphql';
44
import { createRequiredContext } from '~/utils/createRequiredContext';
55

6-
type RecordTableContextValue = {
6+
export type RecordTableContextValue = {
77
recordTableId: string;
88
viewBarId: string;
99
objectNameSingular: string;

packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellFieldContextWrapper.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { useRecordTableRowContextOrThrow } from '@/object-record/record-table/co
66
import { RecordTableCellFieldContextGeneric } from '@/object-record/record-table/record-table-cell/components/RecordTableCellFieldContextGeneric';
77
import { RecordTableCellFieldContextLabelIdentifier } from '@/object-record/record-table/record-table-cell/components/RecordTableCellFieldContextLabelIdentifier';
88
import { getRecordFieldInputInstanceId } from '@/object-record/utils/getRecordFieldInputId';
9-
import { type ReactNode } from 'react';
9+
import { type ReactNode, useMemo } from 'react';
1010

1111
type RecordTableCellFieldContextWrapperProps = {
1212
children: ReactNode;
@@ -36,8 +36,15 @@ export const RecordTableCellFieldContextWrapper = ({
3636
const isLabelIdentifier =
3737
labelIdentifierFieldMetadataItem?.id === recordField.fieldMetadataItemId;
3838

39+
const fieldComponentInstanceContextValue = useMemo(
40+
() => ({ instanceId }),
41+
[instanceId],
42+
);
43+
3944
return (
40-
<RecordFieldComponentInstanceContext.Provider value={{ instanceId }}>
45+
<RecordFieldComponentInstanceContext.Provider
46+
value={fieldComponentInstanceContextValue}
47+
>
4148
{isLabelIdentifier ? (
4249
<RecordTableCellFieldContextLabelIdentifier key={instanceId}>
4350
{children}

packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellWrapper.tsx

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import { type RecordField } from '@/object-record/record-field/types/RecordField';
2-
import { RecordTableCellContext } from '@/object-record/record-table/contexts/RecordTableCellContext';
2+
import {
3+
RecordTableCellContext,
4+
type RecordTableCellContextValue,
5+
} from '@/object-record/record-table/contexts/RecordTableCellContext';
36
import { useRecordTableRowContextOrThrow } from '@/object-record/record-table/contexts/RecordTableRowContext';
47
import { RecordTableCellFieldContextWrapper } from '@/object-record/record-table/record-table-cell/components/RecordTableCellFieldContextWrapper';
5-
import { type TableCellPosition } from '@/object-record/record-table/types/TableCellPosition';
68
import { useMemo } from 'react';
79

810
export const RecordTableCellWrapper = ({
@@ -16,20 +18,20 @@ export const RecordTableCellWrapper = ({
1618
}) => {
1719
const { rowIndex } = useRecordTableRowContextOrThrow();
1820

19-
const currentTableCellPosition: TableCellPosition = useMemo(
21+
const cellContextValue: RecordTableCellContextValue = useMemo(
2022
() => ({
21-
column: recordFieldIndex,
22-
row: rowIndex,
23+
recordField,
24+
cellPosition: {
25+
column: recordFieldIndex,
26+
row: rowIndex,
27+
},
2328
}),
24-
[recordFieldIndex, rowIndex],
29+
[recordField, recordFieldIndex, rowIndex],
2530
);
2631

2732
return (
2833
<RecordTableCellContext.Provider
29-
value={{
30-
recordField,
31-
cellPosition: currentTableCellPosition,
32-
}}
34+
value={cellContextValue}
3335
key={recordField.fieldMetadataItemId}
3436
>
3537
<RecordTableCellFieldContextWrapper recordField={recordField}>

0 commit comments

Comments
 (0)