From 9b95d28cb346f27c82abbea569a24930634c747b Mon Sep 17 00:00:00 2001 From: Lucas Bordeau Date: Mon, 12 Jan 2026 12:41:59 +0100 Subject: [PATCH] Refactored table virtualization real index state (#17074) This PR refactors the table virtualization real index main state that was creating performance problems. The problem was that we kept track of all the real rows even if we only print 200 at a time in the application. This caused a crash when some users with 10k+ rows on a table tried to iterate over this state 10.000 times or more. The trade-off is that now we keep all real indices in a Map, and each virtual row uses a selector to go to this Map. This way if we want to reset the full Map, we just have to overwrite the base state that contains the map, and the 200 selectors will recompute. This happens for example when we delete a row. This now has a limited and negligible performance impact. --- ...WithoutGroupDragAndDropOptimisticUpdate.ts | 14 ++- .../hooks/internal/useSetRecordTableData.ts | 13 +-- ...cordTableBodyVirtualizedDraggableClone.tsx | 6 +- .../components/RecordTableTr.tsx | 9 +- .../hooks/useIsRecordSecondaryDragged.ts | 3 +- ...rdIdFirstOfGroupComponentFamilySelector.ts | 12 ++- ...ecordTableRowVirtualizedDebugRowHelper.tsx | 15 ++- .../RecordTableRowVirtualizedFullData.tsx | 8 +- .../RecordTableRowVirtualizedRouterLevel2.tsx | 8 +- .../hooks/useLoadRecordsToVirtualRows.ts | 24 ++--- ...seResetVirtualizationBecauseDataChanged.ts | 98 ++++++++++++++++++- .../useTriggerInitialRecordTableDataLoad.ts | 34 +++---- ...tatusByRealIndexComponentFamilySelector.ts | 67 +++++++++++++ ...ngStatusByRealIndexComponentFamilyState.ts | 9 -- ...aLoadingStatusByRealIndexComponentState.ts | 10 ++ .../realIndexByRecordIdComponentSelector.ts | 46 --------- ...ordIdByRealIndexComponentFamilySelector.ts | 58 +++++++++++ ...recordIdByRealIndexComponentFamilyState.ts | 9 -- .../recordIdByRealIndexComponentState.ts | 10 ++ 19 files changed, 320 insertions(+), 133 deletions(-) create mode 100644 packages/twenty-front/src/modules/object-record/record-table/virtualization/states/dataLoadingStatusByRealIndexComponentFamilySelector.ts delete mode 100644 packages/twenty-front/src/modules/object-record/record-table/virtualization/states/dataLoadingStatusByRealIndexComponentFamilyState.ts create mode 100644 packages/twenty-front/src/modules/object-record/record-table/virtualization/states/dataLoadingStatusByRealIndexComponentState.ts delete mode 100644 packages/twenty-front/src/modules/object-record/record-table/virtualization/states/realIndexByRecordIdComponentSelector.ts create mode 100644 packages/twenty-front/src/modules/object-record/record-table/virtualization/states/recordIdByRealIndexComponentFamilySelector.ts delete mode 100644 packages/twenty-front/src/modules/object-record/record-table/virtualization/states/recordIdByRealIndexComponentFamilyState.ts create mode 100644 packages/twenty-front/src/modules/object-record/record-table/virtualization/states/recordIdByRealIndexComponentState.ts diff --git a/packages/twenty-front/src/modules/object-record/record-drag/hooks/useTriggerTableWithoutGroupDragAndDropOptimisticUpdate.ts b/packages/twenty-front/src/modules/object-record/record-drag/hooks/useTriggerTableWithoutGroupDragAndDropOptimisticUpdate.ts index 8671cf2bd5..30ec19f5bf 100644 --- a/packages/twenty-front/src/modules/object-record/record-drag/hooks/useTriggerTableWithoutGroupDragAndDropOptimisticUpdate.ts +++ b/packages/twenty-front/src/modules/object-record/record-drag/hooks/useTriggerTableWithoutGroupDragAndDropOptimisticUpdate.ts @@ -1,12 +1,14 @@ import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState'; import { useLoadRecordsToVirtualRows } from '@/object-record/record-table/virtualization/hooks/useLoadRecordsToVirtualRows'; import { lastScrollPositionComponentState } from '@/object-record/record-table/virtualization/states/lastScrollPositionComponentState'; -import { recordIdByRealIndexComponentFamilyState } from '@/object-record/record-table/virtualization/states/recordIdByRealIndexComponentFamilyState'; +import { recordIdByRealIndexComponentFamilySelector } from '@/object-record/record-table/virtualization/states/recordIdByRealIndexComponentFamilySelector'; + import { totalNumberOfRecordsToVirtualizeComponentState } from '@/object-record/record-table/virtualization/states/totalNumberOfRecordsToVirtualizeComponentState'; import { getVirtualizationOverscanWindow } from '@/object-record/record-table/virtualization/utils/getVirtualizationOverscanWindow'; import { type RecordWithPosition } from '@/object-record/utils/computeNewPositionOfDraggedRecord'; import { useScrollWrapperHTMLElement } from '@/ui/utilities/scroll/hooks/useScrollWrapperHTMLElement'; import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState'; +import { useRecoilComponentFamilyCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyCallbackState'; import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue'; import { useRecoilCallback } from 'recoil'; import { findById, isDefined } from 'twenty-shared/utils'; @@ -15,8 +17,10 @@ import { sortByProperty } from '~/utils/array/sortByProperty'; // TODO: does not work when scrolling while dragging and does not work if not paired with a network refetch right after // But it's sufficient right now for the main use case export const useTriggerTableWithoutGroupDragAndDropOptimisticUpdate = () => { - const recordIdByRealIndexCallbackFamilyState = - useRecoilComponentCallbackState(recordIdByRealIndexComponentFamilyState); + const recordIdByRealIndexCallbackSelector = + useRecoilComponentFamilyCallbackState( + recordIdByRealIndexComponentFamilySelector, + ); const lastScrollPositionCallbackState = useRecoilComponentCallbackState( lastScrollPositionComponentState, @@ -64,7 +68,7 @@ export const useTriggerTableWithoutGroupDragAndDropOptimisticUpdate = () => { ) { const recordIdAtRealIndex = getSnapshotValue( snapshot, - recordIdByRealIndexCallbackFamilyState({ realIndex }), + recordIdByRealIndexCallbackSelector(realIndex), ); if (!isDefined(recordIdAtRealIndex)) { @@ -115,7 +119,7 @@ export const useTriggerTableWithoutGroupDragAndDropOptimisticUpdate = () => { [ lastScrollPositionCallbackState, loadRecordsToVirtualRows, - recordIdByRealIndexCallbackFamilyState, + recordIdByRealIndexCallbackSelector, scrollWrapperHTMLElement?.clientHeight, totalNumberOfRecordsToVirtualizeCallbackState, ], diff --git a/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/useSetRecordTableData.ts b/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/useSetRecordTableData.ts index 8b8aea5889..4949eb1e81 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/useSetRecordTableData.ts +++ b/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/useSetRecordTableData.ts @@ -10,7 +10,8 @@ import { hasUserSelectedAllRowsComponentState } from '@/object-record/record-tab import { isRowSelectedComponentFamilyState } from '@/object-record/record-table/record-table-row/states/isRowSelectedComponentFamilyState'; import { isRecordTableInitialLoadingComponentState } from '@/object-record/record-table/states/isRecordTableInitialLoadingComponentState'; import { recordTableHoverPositionComponentState } from '@/object-record/record-table/states/recordTableHoverPositionComponentState'; -import { recordIdByRealIndexComponentFamilyState } from '@/object-record/record-table/virtualization/states/recordIdByRealIndexComponentFamilyState'; + +import { recordIdByRealIndexComponentFamilySelector } from '@/object-record/record-table/virtualization/states/recordIdByRealIndexComponentFamilySelector'; import { type ObjectRecord } from '@/object-record/types/ObjectRecord'; import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState'; import { useRecoilComponentFamilyCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyCallbackState'; @@ -53,9 +54,9 @@ export const useSetRecordTableData = ({ recordTableId, ); - const recordIdByRealIndexCallbackState = + const recordIdByRealIndexCallbackSelector = useRecoilComponentFamilyCallbackState( - recordIdByRealIndexComponentFamilyState, + recordIdByRealIndexComponentFamilySelector, recordTableId, ); @@ -135,11 +136,11 @@ export const useSetRecordTableData = ({ for (const [realIndex, recordId] of recordIds.entries()) { const currentRecordIdAtRealIndex = getSnapshotValue( snapshot, - recordIdByRealIndexCallbackState({ realIndex }), + recordIdByRealIndexCallbackSelector(realIndex), ); if (recordId !== currentRecordIdAtRealIndex) { - set(recordIdByRealIndexCallbackState({ realIndex }), recordId); + set(recordIdByRealIndexCallbackSelector(realIndex), recordId); } } } @@ -153,7 +154,7 @@ export const useSetRecordTableData = ({ unfocusRecordTableRow, setRecordTableHoverPosition, isRowSelectedFamilyState, - recordIdByRealIndexCallbackState, + recordIdByRealIndexCallbackSelector, isRecordTableInitialLoadingCallbackState, ], ); diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyVirtualizedDraggableClone.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyVirtualizedDraggableClone.tsx index 2eff949f82..e823073745 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyVirtualizedDraggableClone.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyVirtualizedDraggableClone.tsx @@ -25,7 +25,7 @@ import { RecordTableTr } from '@/object-record/record-table/record-table-row/com import { useIsTableRowSecondaryDragged } from '@/object-record/record-table/record-table-row/hooks/useIsRecordSecondaryDragged'; import { getRecordTableColumnFieldWidthClassName } from '@/object-record/record-table/utils/getRecordTableColumnFieldWidthClassName'; import { getRecordTableColumnFieldWidthCSSVariableName } from '@/object-record/record-table/utils/getRecordTableColumnFieldWidthCSSVariableName'; -import { recordIdByRealIndexComponentFamilyState } from '@/object-record/record-table/virtualization/states/recordIdByRealIndexComponentFamilyState'; +import { recordIdByRealIndexComponentFamilySelector } from '@/object-record/record-table/virtualization/states/recordIdByRealIndexComponentFamilySelector'; import { useRecoilComponentFamilyValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValue'; import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; @@ -148,8 +148,8 @@ export const RecordTableBodyVirtualizedDraggableClone = ({ const theme = useTheme(); const recordId = useRecoilComponentFamilyValue( - recordIdByRealIndexComponentFamilyState, - { realIndex }, + recordIdByRealIndexComponentFamilySelector, + realIndex, ); const { lastColumnWidth } = useRecordTableLastColumnWidthToFill(); diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-row/components/RecordTableTr.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-row/components/RecordTableTr.tsx index 0ca227a617..08d4532ad3 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-row/components/RecordTableTr.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-row/components/RecordTableTr.tsx @@ -9,7 +9,8 @@ import { isRowSelectedComponentFamilyState } from '@/object-record/record-table/ import { isRecordTableRowActiveComponentFamilyState } from '@/object-record/record-table/states/isRecordTableRowActiveComponentFamilyState'; import { isRecordTableRowFocusActiveComponentState } from '@/object-record/record-table/states/isRecordTableRowFocusActiveComponentState'; import { isRecordTableRowFocusedComponentFamilyState } from '@/object-record/record-table/states/isRecordTableRowFocusedComponentFamilyState'; -import { recordIdByRealIndexComponentFamilyState } from '@/object-record/record-table/virtualization/states/recordIdByRealIndexComponentFamilyState'; +import { recordIdByRealIndexComponentFamilySelector } from '@/object-record/record-table/virtualization/states/recordIdByRealIndexComponentFamilySelector'; + import { useRecoilComponentFamilyValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValue'; import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; import { forwardRef, type ReactNode } from 'react'; @@ -55,13 +56,13 @@ export const RecordTableTr = forwardRef( ); const nextRecordId = useRecoilComponentFamilyValue( - recordIdByRealIndexComponentFamilyState, - { realIndex: focusIndex + 1 }, + recordIdByRealIndexComponentFamilySelector, + focusIndex + 1, ); const isNextRecordIdFirstOfGroup = useRecoilComponentFamilyValue( isRecordIdFirstOfGroupComponentFamilySelector, - { recordId: nextRecordId ?? '' }, + nextRecordId, ); const isFocused = useRecoilComponentFamilyValue( diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-row/hooks/useIsRecordSecondaryDragged.ts b/packages/twenty-front/src/modules/object-record/record-table/record-table-row/hooks/useIsRecordSecondaryDragged.ts index 96f1039942..733b23e6f3 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-row/hooks/useIsRecordSecondaryDragged.ts +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-row/hooks/useIsRecordSecondaryDragged.ts @@ -1,7 +1,8 @@ import { isRecordIdSecondaryDragMultipleComponentFamilyState } from '@/object-record/record-drag/states/isRecordIdSecondaryDragMultipleComponentFamilyState'; import { useRecoilComponentFamilyValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValue'; +import { type Nullable } from 'twenty-shared/types'; -export const useIsTableRowSecondaryDragged = (recordId: string | null) => { +export const useIsTableRowSecondaryDragged = (recordId: Nullable) => { const isSecondaryDragged = useRecoilComponentFamilyValue( isRecordIdSecondaryDragMultipleComponentFamilyState, { recordId: recordId ?? '' }, diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-row/states/isRecordIdFirstOfGroupComponentFamilySelector.ts b/packages/twenty-front/src/modules/object-record/record-table/record-table-row/states/isRecordIdFirstOfGroupComponentFamilySelector.ts index f68b5ce80d..e6d68526b6 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-row/states/isRecordIdFirstOfGroupComponentFamilySelector.ts +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-row/states/isRecordIdFirstOfGroupComponentFamilySelector.ts @@ -3,9 +3,11 @@ import { recordIndexRecordIdsByGroupComponentFamilyState } from '@/object-record import { recordIndexAllRecordIdsComponentSelector } from '@/object-record/record-index/states/selectors/recordIndexAllRecordIdsComponentSelector'; import { createComponentFamilySelector } from '@/ui/utilities/state/component-state/utils/createComponentFamilySelector'; import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext'; +import { type Nullable } from 'twenty-shared/types'; +import { isDefined } from 'twenty-shared/utils'; export const isRecordIdFirstOfGroupComponentFamilySelector = - createComponentFamilySelector({ + createComponentFamilySelector>({ key: 'isRecordIdFirstOfGroupComponentFamilySelector', componentInstanceContext: ViewComponentInstanceContext, get: @@ -19,6 +21,10 @@ export const isRecordIdFirstOfGroupComponentFamilySelector = const hasRecordGroups = recordGroupIds.length > 0; + if (!isDefined(familyKey)) { + return false; + } + if (hasRecordGroups) { for (const recordGroupId of recordGroupIds) { const recordIdsForThisGroup = get( @@ -28,7 +34,7 @@ export const isRecordIdFirstOfGroupComponentFamilySelector = }), ); - if (recordIdsForThisGroup[0] === familyKey.recordId) { + if (recordIdsForThisGroup[0] === familyKey) { return true; } } @@ -39,7 +45,7 @@ export const isRecordIdFirstOfGroupComponentFamilySelector = }), ); - if (allRecordIds[0] === familyKey.recordId) { + if (allRecordIds[0] === familyKey) { return true; } } diff --git a/packages/twenty-front/src/modules/object-record/record-table/virtualization/components/RecordTableRowVirtualizedDebugRowHelper.tsx b/packages/twenty-front/src/modules/object-record/record-table/virtualization/components/RecordTableRowVirtualizedDebugRowHelper.tsx index 1dd0bd1d1b..4ed35cdff2 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/virtualization/components/RecordTableRowVirtualizedDebugRowHelper.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/virtualization/components/RecordTableRowVirtualizedDebugRowHelper.tsx @@ -3,8 +3,9 @@ import { getLabelIdentifierFieldValue } from '@/object-metadata/utils/getLabelId import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState'; import { RECORD_TABLE_ROW_HEIGHT } from '@/object-record/record-table/constants/RecordTableRowHeight'; import { useRecordTableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableContext'; +import { dataLoadingStatusByRealIndexComponentFamilySelector } from '@/object-record/record-table/virtualization/states/dataLoadingStatusByRealIndexComponentFamilySelector'; import { realIndexByVirtualIndexComponentFamilyState } from '@/object-record/record-table/virtualization/states/realIndexByVirtualIndexComponentFamilyState'; -import { recordIdByRealIndexComponentFamilyState } from '@/object-record/record-table/virtualization/states/recordIdByRealIndexComponentFamilyState'; +import { recordIdByRealIndexComponentFamilySelector } from '@/object-record/record-table/virtualization/states/recordIdByRealIndexComponentFamilySelector'; import { useRecoilComponentFamilyValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValue'; import styled from '@emotion/styled'; @@ -50,8 +51,13 @@ export const RecordTableRowVirtualizedDebugRowHelper = ({ ); const recordId = useRecoilComponentFamilyValue( - recordIdByRealIndexComponentFamilyState, - { realIndex }, + recordIdByRealIndexComponentFamilySelector, + realIndex, + ); + + const dataLoadingStatus = useRecoilComponentFamilyValue( + dataLoadingStatusByRealIndexComponentFamilySelector, + realIndex, ); const pixelsFromTop = @@ -85,6 +91,9 @@ export const RecordTableRowVirtualizedDebugRowHelper = ({ id: {recordId} + + status :{isDefined(dataLoadingStatus) ? dataLoadingStatus : 'undefined'} + ); }; diff --git a/packages/twenty-front/src/modules/object-record/record-table/virtualization/components/RecordTableRowVirtualizedFullData.tsx b/packages/twenty-front/src/modules/object-record/record-table/virtualization/components/RecordTableRowVirtualizedFullData.tsx index f568c2f0c0..e7a13c9094 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/virtualization/components/RecordTableRowVirtualizedFullData.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/virtualization/components/RecordTableRowVirtualizedFullData.tsx @@ -9,7 +9,7 @@ import { RecordTableRowHotkeyEffect } from '@/object-record/record-table/record- import { isRecordTableRowFocusActiveComponentState } from '@/object-record/record-table/states/isRecordTableRowFocusActiveComponentState'; import { isRecordTableRowFocusedComponentFamilyState } from '@/object-record/record-table/states/isRecordTableRowFocusedComponentFamilyState'; import { RecordTableRowVirtualizedSkeleton } from '@/object-record/record-table/virtualization/components/RecordTableRowVirtualizedSkeleton'; -import { recordIdByRealIndexComponentFamilyState } from '@/object-record/record-table/virtualization/states/recordIdByRealIndexComponentFamilyState'; +import { recordIdByRealIndexComponentFamilySelector } from '@/object-record/record-table/virtualization/states/recordIdByRealIndexComponentFamilySelector'; import { useRecoilComponentFamilyValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValue'; import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; @@ -25,7 +25,7 @@ export const RecordTableRowVirtualizedFullData = ({ }: RecordTableRowVirtualizedFullDataProps) => { const isFocused = useRecoilComponentFamilyValue( isRecordTableRowFocusedComponentFamilyState, - realIndex ?? 0, + realIndex, ); const isRowFocusActive = useRecoilComponentValue( @@ -33,8 +33,8 @@ export const RecordTableRowVirtualizedFullData = ({ ); const recordId = useRecoilComponentFamilyValue( - recordIdByRealIndexComponentFamilyState, - { realIndex }, + recordIdByRealIndexComponentFamilySelector, + realIndex, ); if (!isDefined(recordId)) { diff --git a/packages/twenty-front/src/modules/object-record/record-table/virtualization/components/RecordTableRowVirtualizedRouterLevel2.tsx b/packages/twenty-front/src/modules/object-record/record-table/virtualization/components/RecordTableRowVirtualizedRouterLevel2.tsx index 5d18058e03..91e47be2c2 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/virtualization/components/RecordTableRowVirtualizedRouterLevel2.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/virtualization/components/RecordTableRowVirtualizedRouterLevel2.tsx @@ -1,7 +1,7 @@ import { RecordTableRowVirtualizedFullData } from '@/object-record/record-table/virtualization/components/RecordTableRowVirtualizedFullData'; import { RecordTableRowVirtualizedSkeleton } from '@/object-record/record-table/virtualization/components/RecordTableRowVirtualizedSkeleton'; -import { dataLoadingStatusByRealIndexComponentFamilyState } from '@/object-record/record-table/virtualization/states/dataLoadingStatusByRealIndexComponentFamilyState'; +import { dataLoadingStatusByRealIndexComponentFamilySelector } from '@/object-record/record-table/virtualization/states/dataLoadingStatusByRealIndexComponentFamilySelector'; import { useRecoilComponentFamilyValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValue'; @@ -13,11 +13,11 @@ export const RecordTableRowVirtualizedRouterLevel2 = ({ realIndex, }: RecordTableRowVirtualizedRouterLevel2Props) => { const dataLoadingStatus = useRecoilComponentFamilyValue( - dataLoadingStatusByRealIndexComponentFamilyState, - { realIndex }, + dataLoadingStatusByRealIndexComponentFamilySelector, + realIndex, ); - if (dataLoadingStatus === null) { + if (dataLoadingStatus !== 'loaded') { return ; } diff --git a/packages/twenty-front/src/modules/object-record/record-table/virtualization/hooks/useLoadRecordsToVirtualRows.ts b/packages/twenty-front/src/modules/object-record/record-table/virtualization/hooks/useLoadRecordsToVirtualRows.ts index cb42ebc7d8..5cd27b00bb 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/virtualization/hooks/useLoadRecordsToVirtualRows.ts +++ b/packages/twenty-front/src/modules/object-record/record-table/virtualization/hooks/useLoadRecordsToVirtualRows.ts @@ -3,22 +3,22 @@ import { useRecoilCallback } from 'recoil'; import { recordIndexAllRecordIdsComponentSelector } from '@/object-record/record-index/states/selectors/recordIndexAllRecordIdsComponentSelector'; import { hasUserSelectedAllRowsComponentState } from '@/object-record/record-table/record-table-row/states/hasUserSelectedAllRowsFamilyState'; import { isRowSelectedComponentFamilyState } from '@/object-record/record-table/record-table-row/states/isRowSelectedComponentFamilyState'; -import { dataLoadingStatusByRealIndexComponentFamilyState } from '@/object-record/record-table/virtualization/states/dataLoadingStatusByRealIndexComponentFamilyState'; -import { recordIdByRealIndexComponentFamilyState } from '@/object-record/record-table/virtualization/states/recordIdByRealIndexComponentFamilyState'; +import { dataLoadingStatusByRealIndexComponentFamilySelector } from '@/object-record/record-table/virtualization/states/dataLoadingStatusByRealIndexComponentFamilySelector'; +import { recordIdByRealIndexComponentFamilySelector } from '@/object-record/record-table/virtualization/states/recordIdByRealIndexComponentFamilySelector'; import { type ObjectRecord } from '@/object-record/types/ObjectRecord'; import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState'; import { useRecoilComponentFamilyCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyCallbackState'; import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue'; export const useLoadRecordsToVirtualRows = () => { - const recordIdByRealIndexCallbackState = + const recordIdByRealIndexCallbackSelector = useRecoilComponentFamilyCallbackState( - recordIdByRealIndexComponentFamilyState, + recordIdByRealIndexComponentFamilySelector, ); - const dataLoadingStatusByRealIndexCallbackState = - useRecoilComponentCallbackState( - dataLoadingStatusByRealIndexComponentFamilyState, + const dataLoadingStatusByRealIndexCallbackSelector = + useRecoilComponentFamilyCallbackState( + dataLoadingStatusByRealIndexComponentFamilySelector, ); const recordIndexAllRecordIdsSelector = useRecoilComponentCallbackState( @@ -52,15 +52,15 @@ export const useLoadRecordsToVirtualRows = () => { const currentRecordIdAtRealIndex = getSnapshotValue( snapshot, - recordIdByRealIndexCallbackState({ realIndex }), + recordIdByRealIndexCallbackSelector(realIndex), ); if (record.id !== currentRecordIdAtRealIndex) { - set(recordIdByRealIndexCallbackState({ realIndex }), record.id); + set(recordIdByRealIndexCallbackSelector(realIndex), record.id); } set( - dataLoadingStatusByRealIndexCallbackState({ realIndex }), + dataLoadingStatusByRealIndexCallbackSelector(realIndex), 'loaded', ); } @@ -85,8 +85,8 @@ export const useLoadRecordsToVirtualRows = () => { set(recordIndexAllRecordIdsSelector, newAllRecordIds); }, [ - recordIdByRealIndexCallbackState, - dataLoadingStatusByRealIndexCallbackState, + recordIdByRealIndexCallbackSelector, + dataLoadingStatusByRealIndexCallbackSelector, recordIndexAllRecordIdsSelector, isRowSelectedCallbackState, hasUserSelectedAllRowsCallbackState, diff --git a/packages/twenty-front/src/modules/object-record/record-table/virtualization/hooks/useResetVirtualizationBecauseDataChanged.ts b/packages/twenty-front/src/modules/object-record/record-table/virtualization/hooks/useResetVirtualizationBecauseDataChanged.ts index e6ed969ac6..cd82e8012c 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/virtualization/hooks/useResetVirtualizationBecauseDataChanged.ts +++ b/packages/twenty-front/src/modules/object-record/record-table/virtualization/hooks/useResetVirtualizationBecauseDataChanged.ts @@ -4,11 +4,18 @@ import { useRecordsFieldVisibleGqlFields } from '@/object-record/record-field/ho import { useFindManyRecordIndexTableParams } from '@/object-record/record-index/hooks/useFindManyRecordIndexTableParams'; import { useTriggerFetchPages } from '@/object-record/record-table/virtualization/hooks/useTriggerFetchPages'; +import { dataLoadingStatusByRealIndexComponentState } from '@/object-record/record-table/virtualization/states/dataLoadingStatusByRealIndexComponentState'; import { dataPagesLoadedComponentState } from '@/object-record/record-table/virtualization/states/dataPagesLoadedComponentState'; +import { lastScrollPositionComponentState } from '@/object-record/record-table/virtualization/states/lastScrollPositionComponentState'; +import { recordIdByRealIndexComponentState } from '@/object-record/record-table/virtualization/states/recordIdByRealIndexComponentState'; import { totalNumberOfRecordsToVirtualizeComponentState } from '@/object-record/record-table/virtualization/states/totalNumberOfRecordsToVirtualizeComponentState'; +import { getVirtualizationOverscanWindow } from '@/object-record/record-table/virtualization/utils/getVirtualizationOverscanWindow'; +import { useScrollWrapperHTMLElement } from '@/ui/utilities/scroll/hooks/useScrollWrapperHTMLElement'; import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState'; +import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue'; import { useCallback } from 'react'; import { useRecoilCallback } from 'recoil'; +import { isDefined } from 'twenty-shared/utils'; import { sleep } from '~/utils/sleep'; export const useResetVirtualizationBecauseDataChanged = ( @@ -18,6 +25,8 @@ export const useResetVirtualizationBecauseDataChanged = ( objectNameSingular, }); + const { scrollWrapperHTMLElement } = useScrollWrapperHTMLElement(); + const params = useFindManyRecordIndexTableParams(objectNameSingular); // TODO: we could optimize this by using an aggregate or using only id: true in recordGqlFields @@ -43,18 +52,101 @@ export const useResetVirtualizationBecauseDataChanged = ( const { triggerFetchPagesWithoutDebounce } = useTriggerFetchPages(); + const lastScrollPositionCallbackState = useRecoilComponentCallbackState( + lastScrollPositionComponentState, + ); + + const recordIdByRealIndexCallbackState = useRecoilComponentCallbackState( + recordIdByRealIndexComponentState, + ); + + const dataLoadingStatusByRealIndexCallbackState = + useRecoilComponentCallbackState(dataLoadingStatusByRealIndexComponentState); + const resetVirtualization = useRecoilCallback( - ({ set }) => + ({ set, snapshot }) => async () => { const { totalCount } = await findManyRecordsLazy(); + const tableScrollWrapperHeight = + scrollWrapperHTMLElement?.clientHeight ?? 0; + + const lastScrollPosition = getSnapshotValue( + snapshot, + lastScrollPositionCallbackState, + ); + + const totalNumberOfRecordsToVirtualize = + getSnapshotValue( + snapshot, + totalNumberOfRecordsToVirtualizeCallbackState, + ) ?? 0; + + const { + firstRealIndexInOverscanWindow, + lastRealIndexInOverscanWindow, + } = getVirtualizationOverscanWindow( + lastScrollPosition, + tableScrollWrapperHeight, + totalNumberOfRecordsToVirtualize, + ); + + const recordIdByRealIndex = getSnapshotValue( + snapshot, + recordIdByRealIndexCallbackState, + ); + + const dataLoadingStatusByRealIndex = getSnapshotValue( + snapshot, + dataLoadingStatusByRealIndexCallbackState, + ); + + const lengthOfOverscanWindow = + lastRealIndexInOverscanWindow - firstRealIndexInOverscanWindow + 1; + + const newRecordIdByRealIndex = new Map(); + const newDataLoadingStatusByRealIndex = new Map< + number, + 'loaded' | 'not-loaded' + >(); + + for (let i = 0; i < lengthOfOverscanWindow; i++) { + const realIndex = firstRealIndexInOverscanWindow + i; + + const existingRecordId = recordIdByRealIndex.get(realIndex); + + if (isDefined(existingRecordId)) { + newRecordIdByRealIndex.set(realIndex, existingRecordId); + } + + const existingDataLoadingStatus = + dataLoadingStatusByRealIndex.get(realIndex); + + if (isDefined(existingDataLoadingStatus)) { + newDataLoadingStatusByRealIndex.set( + realIndex, + existingDataLoadingStatus, + ); + } + } + + set(recordIdByRealIndexCallbackState, newRecordIdByRealIndex); + set( + dataLoadingStatusByRealIndexCallbackState, + newDataLoadingStatusByRealIndex, + ); + set(dataPagesLoadedCallbackState, []); set(totalNumberOfRecordsToVirtualizeCallbackState, totalCount); }, [ - dataPagesLoadedCallbackState, - totalNumberOfRecordsToVirtualizeCallbackState, findManyRecordsLazy, + scrollWrapperHTMLElement?.clientHeight, + lastScrollPositionCallbackState, + totalNumberOfRecordsToVirtualizeCallbackState, + dataPagesLoadedCallbackState, + dataLoadingStatusByRealIndexCallbackState, + recordIdByRealIndexCallbackState, ], ); diff --git a/packages/twenty-front/src/modules/object-record/record-table/virtualization/hooks/useTriggerInitialRecordTableDataLoad.ts b/packages/twenty-front/src/modules/object-record/record-table/virtualization/hooks/useTriggerInitialRecordTableDataLoad.ts index 9c3940fd2b..69dd20cc78 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/virtualization/hooks/useTriggerInitialRecordTableDataLoad.ts +++ b/packages/twenty-front/src/modules/object-record/record-table/virtualization/hooks/useTriggerInitialRecordTableDataLoad.ts @@ -14,12 +14,12 @@ import { useReapplyRowSelection } from '@/object-record/record-table/virtualizat import { useResetTableFocuses } from '@/object-record/record-table/virtualization/hooks/useResetTableFocuses'; import { useResetVirtualizedRowTreadmill } from '@/object-record/record-table/virtualization/hooks/useResetVirtualizedRowTreadmill'; -import { dataLoadingStatusByRealIndexComponentFamilyState } from '@/object-record/record-table/virtualization/states/dataLoadingStatusByRealIndexComponentFamilyState'; +import { dataLoadingStatusByRealIndexComponentFamilySelector } from '@/object-record/record-table/virtualization/states/dataLoadingStatusByRealIndexComponentFamilySelector'; import { dataPagesLoadedComponentState } from '@/object-record/record-table/virtualization/states/dataPagesLoadedComponentState'; import { isInitializingVirtualTableDataLoadingComponentState } from '@/object-record/record-table/virtualization/states/isInitializingVirtualTableDataLoadingComponentState'; import { lastRealIndexSetComponentState } from '@/object-record/record-table/virtualization/states/lastRealIndexSetComponentState'; import { lastScrollPositionComponentState } from '@/object-record/record-table/virtualization/states/lastScrollPositionComponentState'; -import { recordIdByRealIndexComponentFamilyState } from '@/object-record/record-table/virtualization/states/recordIdByRealIndexComponentFamilyState'; +import { recordIdByRealIndexComponentFamilySelector } from '@/object-record/record-table/virtualization/states/recordIdByRealIndexComponentFamilySelector'; import { scrollAtRealIndexComponentState } from '@/object-record/record-table/virtualization/states/scrollAtRealIndexComponentState'; import { totalNumberOfRecordsToVirtualizeComponentState } from '@/object-record/record-table/virtualization/states/totalNumberOfRecordsToVirtualizeComponentState'; import { type ObjectRecord } from '@/object-record/types/ObjectRecord'; @@ -56,14 +56,14 @@ export const useTriggerInitialRecordTableDataLoad = () => { recordIndexAllRecordIdsComponentSelector, ); - const recordIdByRealIndexCallbackState = + const recordIdByRealIndexCallbackSelector = useRecoilComponentFamilyCallbackState( - recordIdByRealIndexComponentFamilyState, + recordIdByRealIndexComponentFamilySelector, ); - const dataLoadingStatusByRealIndexCallbackState = - useRecoilComponentCallbackState( - dataLoadingStatusByRealIndexComponentFamilyState, + const dataLoadingStatusByRealIndexCallbackSelector = + useRecoilComponentFamilyCallbackState( + dataLoadingStatusByRealIndexComponentFamilySelector, ); const setIsRecordTableScrolledHorizontally = useSetRecoilComponentState( @@ -144,20 +144,12 @@ export const useTriggerInitialRecordTableDataLoad = () => { records = SIGN_IN_BACKGROUND_MOCK_COMPANIES; totalCount = SIGN_IN_BACKGROUND_MOCK_COMPANIES.length; } else { - for (const [index] of currentRecordIds.entries()) { + for (const [realIndex] of currentRecordIds.entries()) { set( - dataLoadingStatusByRealIndexCallbackState({ - realIndex: index, - }), - null, - ); - - set( - recordIdByRealIndexCallbackState({ - realIndex: index, - }), - null, + dataLoadingStatusByRealIndexCallbackSelector(realIndex), + 'not-loaded', ); + set(recordIdByRealIndexCallbackSelector(realIndex), undefined); } const { records: findManyRecords, totalCount: findManyTotalCount } = @@ -215,8 +207,8 @@ export const useTriggerInitialRecordTableDataLoad = () => { setIsRecordTableScrolledVertically, scrollTableToPosition, findManyRecordsLazy, - dataLoadingStatusByRealIndexCallbackState, - recordIdByRealIndexCallbackState, + dataLoadingStatusByRealIndexCallbackSelector, + recordIdByRealIndexCallbackSelector, totalNumberOfRecordsToVirtualizeCallbackState, upsertRecordsInStore, loadRecordsToVirtualRows, diff --git a/packages/twenty-front/src/modules/object-record/record-table/virtualization/states/dataLoadingStatusByRealIndexComponentFamilySelector.ts b/packages/twenty-front/src/modules/object-record/record-table/virtualization/states/dataLoadingStatusByRealIndexComponentFamilySelector.ts new file mode 100644 index 0000000000..a230db5338 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-table/virtualization/states/dataLoadingStatusByRealIndexComponentFamilySelector.ts @@ -0,0 +1,67 @@ +import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext'; +import { dataLoadingStatusByRealIndexComponentState } from '@/object-record/record-table/virtualization/states/dataLoadingStatusByRealIndexComponentState'; +import { createComponentFamilySelector } from '@/ui/utilities/state/component-state/utils/createComponentFamilySelector'; +import { type DefaultValue } from 'recoil'; +import { type Nullable } from 'twenty-shared/types'; +import { isDefined } from 'twenty-shared/utils'; + +export const dataLoadingStatusByRealIndexComponentFamilySelector = + createComponentFamilySelector< + 'loaded' | 'not-loaded' | undefined, + Nullable + >({ + key: 'dataLoadingStatusByRealIndexComponentFamilySelector', + componentInstanceContext: RecordTableComponentInstanceContext, + get: + ({ instanceId, familyKey }) => + ({ get }) => { + const realIndex = familyKey; + + const dataLoadingStatusByRealIndex = get( + dataLoadingStatusByRealIndexComponentState.atomFamily({ + instanceId, + }), + ); + + if (!isDefined(realIndex)) { + return undefined; + } + + const dataLoadingStatus = dataLoadingStatusByRealIndex.get(realIndex); + + return dataLoadingStatus; + }, + set: + ({ familyKey, instanceId }) => + ( + { get, set }, + newValue: 'loaded' | 'not-loaded' | DefaultValue | undefined, + ) => { + if (!isDefined(familyKey)) { + return; + } + + const actualDataLoadingStatusByRealIndex = get( + dataLoadingStatusByRealIndexComponentState.atomFamily({ + instanceId, + }), + ); + + const newDataLoadingStatusByRealIndex = new Map( + actualDataLoadingStatusByRealIndex, + ); + + if (newValue === 'loaded') { + newDataLoadingStatusByRealIndex.set(familyKey, 'loaded'); + } else { + newDataLoadingStatusByRealIndex.set(familyKey, 'not-loaded'); + } + + set( + dataLoadingStatusByRealIndexComponentState.atomFamily({ + instanceId, + }), + newDataLoadingStatusByRealIndex, + ); + }, + }); diff --git a/packages/twenty-front/src/modules/object-record/record-table/virtualization/states/dataLoadingStatusByRealIndexComponentFamilyState.ts b/packages/twenty-front/src/modules/object-record/record-table/virtualization/states/dataLoadingStatusByRealIndexComponentFamilyState.ts deleted file mode 100644 index d080af94f8..0000000000 --- a/packages/twenty-front/src/modules/object-record/record-table/virtualization/states/dataLoadingStatusByRealIndexComponentFamilyState.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext'; -import { createComponentFamilyState } from '@/ui/utilities/state/component-state/utils/createComponentFamilyState'; - -export const dataLoadingStatusByRealIndexComponentFamilyState = - createComponentFamilyState<'loaded' | null, { realIndex: number | null }>({ - key: 'dataLoadingStatusByRealIndexComponentFamilyState', - componentInstanceContext: RecordTableComponentInstanceContext, - defaultValue: null, - }); diff --git a/packages/twenty-front/src/modules/object-record/record-table/virtualization/states/dataLoadingStatusByRealIndexComponentState.ts b/packages/twenty-front/src/modules/object-record/record-table/virtualization/states/dataLoadingStatusByRealIndexComponentState.ts new file mode 100644 index 0000000000..680117d409 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-table/virtualization/states/dataLoadingStatusByRealIndexComponentState.ts @@ -0,0 +1,10 @@ +import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext'; +import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState'; + +export const dataLoadingStatusByRealIndexComponentState = createComponentState< + Map +>({ + key: 'dataLoadingStatusByRealIndexComponentState', + componentInstanceContext: RecordTableComponentInstanceContext, + defaultValue: new Map(), +}); diff --git a/packages/twenty-front/src/modules/object-record/record-table/virtualization/states/realIndexByRecordIdComponentSelector.ts b/packages/twenty-front/src/modules/object-record/record-table/virtualization/states/realIndexByRecordIdComponentSelector.ts deleted file mode 100644 index f11308bf7f..0000000000 --- a/packages/twenty-front/src/modules/object-record/record-table/virtualization/states/realIndexByRecordIdComponentSelector.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext'; -import { recordIdByRealIndexComponentFamilyState } from '@/object-record/record-table/virtualization/states/recordIdByRealIndexComponentFamilyState'; -import { totalNumberOfRecordsToVirtualizeComponentState } from '@/object-record/record-table/virtualization/states/totalNumberOfRecordsToVirtualizeComponentState'; -import { createComponentSelector } from '@/ui/utilities/state/component-state/utils/createComponentSelector'; -import { isDefined } from 'twenty-shared/utils'; - -export const realIndexByRecordIdComponentSelector = createComponentSelector<{ - realIndexByRecordIdMap: Map; -}>({ - key: 'realIndexByRecordIdComponentSelector', - componentInstanceContext: RecordTableComponentInstanceContext, - get: - ({ instanceId }) => - ({ get }) => { - const totalNumberOfRecordsToVirtualize = get( - totalNumberOfRecordsToVirtualizeComponentState.atomFamily({ - instanceId, - }), - ); - - const realIndexByRecordIdMap = new Map(); - - if (!isDefined(totalNumberOfRecordsToVirtualize)) { - return { realIndexByRecordIdMap }; - } - - for ( - let realIndex = 0; - realIndex < totalNumberOfRecordsToVirtualize; - realIndex++ - ) { - const recordId = get( - recordIdByRealIndexComponentFamilyState.atomFamily({ - instanceId, - familyKey: { realIndex }, - }), - ); - - if (isDefined(recordId)) { - realIndexByRecordIdMap.set(recordId, realIndex); - } - } - - return { realIndexByRecordIdMap }; - }, -}); diff --git a/packages/twenty-front/src/modules/object-record/record-table/virtualization/states/recordIdByRealIndexComponentFamilySelector.ts b/packages/twenty-front/src/modules/object-record/record-table/virtualization/states/recordIdByRealIndexComponentFamilySelector.ts new file mode 100644 index 0000000000..f48c5b3fe4 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-table/virtualization/states/recordIdByRealIndexComponentFamilySelector.ts @@ -0,0 +1,58 @@ +import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext'; +import { recordIdByRealIndexComponentState } from '@/object-record/record-table/virtualization/states/recordIdByRealIndexComponentState'; +import { createComponentFamilySelector } from '@/ui/utilities/state/component-state/utils/createComponentFamilySelector'; +import { isNonEmptyString } from '@sniptt/guards'; +import { type DefaultValue } from 'recoil'; +import { type Nullable } from 'twenty-shared/types'; +import { isDefined } from 'twenty-shared/utils'; + +export const recordIdByRealIndexComponentFamilySelector = + createComponentFamilySelector, Nullable>({ + key: 'recordIdByRealIndexComponentFamilySelector', + componentInstanceContext: RecordTableComponentInstanceContext, + get: + ({ instanceId, familyKey }) => + ({ get }) => { + const realIndex = familyKey; + + const recordIdByRealIndex = get( + recordIdByRealIndexComponentState.atomFamily({ + instanceId, + }), + ); + + if (!isDefined(realIndex)) { + return null; + } + + const recordId = recordIdByRealIndex.get(realIndex); + + return recordId; + }, + set: + ({ familyKey, instanceId }) => + ({ get, set }, newValue: Nullable | DefaultValue) => { + if (!isDefined(familyKey)) { + return; + } + + const actualRecordIdByRealIndex = get( + recordIdByRealIndexComponentState.atomFamily({ + instanceId, + }), + ); + + const newRecordIdByRealIndex = new Map(actualRecordIdByRealIndex); + + if (isNonEmptyString(newValue)) { + newRecordIdByRealIndex.set(familyKey, newValue); + } else { + newRecordIdByRealIndex.delete(familyKey); + } + + set( + recordIdByRealIndexComponentState.atomFamily({ instanceId }), + newRecordIdByRealIndex, + ); + }, + }); diff --git a/packages/twenty-front/src/modules/object-record/record-table/virtualization/states/recordIdByRealIndexComponentFamilyState.ts b/packages/twenty-front/src/modules/object-record/record-table/virtualization/states/recordIdByRealIndexComponentFamilyState.ts deleted file mode 100644 index fab769404d..0000000000 --- a/packages/twenty-front/src/modules/object-record/record-table/virtualization/states/recordIdByRealIndexComponentFamilyState.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext'; -import { createComponentFamilyState } from '@/ui/utilities/state/component-state/utils/createComponentFamilyState'; - -export const recordIdByRealIndexComponentFamilyState = - createComponentFamilyState({ - key: 'recordIdByRealIndexComponentFamilyState', - componentInstanceContext: RecordTableComponentInstanceContext, - defaultValue: null, - }); diff --git a/packages/twenty-front/src/modules/object-record/record-table/virtualization/states/recordIdByRealIndexComponentState.ts b/packages/twenty-front/src/modules/object-record/record-table/virtualization/states/recordIdByRealIndexComponentState.ts new file mode 100644 index 0000000000..ad0de49189 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-table/virtualization/states/recordIdByRealIndexComponentState.ts @@ -0,0 +1,10 @@ +import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext'; +import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState'; + +export const recordIdByRealIndexComponentState = createComponentState< + Map +>({ + key: 'recordIdByRealIndexComponentState', + componentInstanceContext: RecordTableComponentInstanceContext, + defaultValue: new Map(), +});