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.
This commit is contained in:
Lucas Bordeau
2026-01-12 12:41:59 +01:00
committed by GitHub
parent 238e6d5cda
commit 9b95d28cb3
19 changed files with 320 additions and 133 deletions
@@ -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,
],
@@ -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,
],
);
@@ -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();
@@ -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<HTMLDivElement, RecordTableTrProps>(
);
const nextRecordId = useRecoilComponentFamilyValue(
recordIdByRealIndexComponentFamilyState,
{ realIndex: focusIndex + 1 },
recordIdByRealIndexComponentFamilySelector,
focusIndex + 1,
);
const isNextRecordIdFirstOfGroup = useRecoilComponentFamilyValue(
isRecordIdFirstOfGroupComponentFamilySelector,
{ recordId: nextRecordId ?? '' },
nextRecordId,
);
const isFocused = useRecoilComponentFamilyValue(
@@ -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<string>) => {
const isSecondaryDragged = useRecoilComponentFamilyValue(
isRecordIdSecondaryDragMultipleComponentFamilyState,
{ recordId: recordId ?? '' },
@@ -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<boolean, { recordId: string }>({
createComponentFamilySelector<boolean, Nullable<string>>({
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;
}
}
@@ -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}
</StyledDebugColumn>
<StyledDebugColumn width={100}>
status :{isDefined(dataLoadingStatus) ? dataLoadingStatus : 'undefined'}
</StyledDebugColumn>
</StyledDebugRow>
);
};
@@ -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)) {
@@ -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 <RecordTableRowVirtualizedSkeleton />;
}
@@ -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,
@@ -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<number, string>();
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,
],
);
@@ -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,
@@ -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<number>
>({
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,
);
},
});
@@ -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,
});
@@ -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<number, 'loaded' | 'not-loaded'>
>({
key: 'dataLoadingStatusByRealIndexComponentState',
componentInstanceContext: RecordTableComponentInstanceContext,
defaultValue: new Map(),
});
@@ -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<string, number>;
}>({
key: 'realIndexByRecordIdComponentSelector',
componentInstanceContext: RecordTableComponentInstanceContext,
get:
({ instanceId }) =>
({ get }) => {
const totalNumberOfRecordsToVirtualize = get(
totalNumberOfRecordsToVirtualizeComponentState.atomFamily({
instanceId,
}),
);
const realIndexByRecordIdMap = new Map<string, number>();
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 };
},
});
@@ -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<string>, Nullable<number>>({
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<string> | 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,
);
},
});
@@ -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<string | null, { realIndex: number | null }>({
key: 'recordIdByRealIndexComponentFamilyState',
componentInstanceContext: RecordTableComponentInstanceContext,
defaultValue: null,
});
@@ -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<number, string>
>({
key: 'recordIdByRealIndexComponentState',
componentInstanceContext: RecordTableComponentInstanceContext,
defaultValue: new Map(),
});