From 81afc8b9cae9e6e59a496c6a8d496cab9baaa9da Mon Sep 17 00:00:00 2001 From: Etienne <45695613+etiennejouan@users.noreply.github.com> Date: Wed, 7 Jan 2026 15:06:32 +0100 Subject: [PATCH] Fix memory crash when creating record in table view (#16984) **Bug Fixed** Creating a new record in the People table view caused a browser memory crash ("Paused before potential out of memory crash"). **Root Cause** In useResetVirtualizationBecauseDataChanged.ts, when creating a record, a loop iterated from 0 to totalNumberOfRecordsToVirtualize (the total database count). **Fix Applied** Changed the loop to only iterate through indices from actually loaded pages Fixes https://github.com/twentyhq/twenty/issues/16980 --- ...seResetVirtualizationBecauseDataChanged.ts | 76 +------------------ 1 file changed, 3 insertions(+), 73 deletions(-) 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 162067833e..e6ed969ac6 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,16 +4,9 @@ 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 { dataLoadingStatusByRealIndexComponentFamilyState } from '@/object-record/record-table/virtualization/states/dataLoadingStatusByRealIndexComponentFamilyState'; import { dataPagesLoadedComponentState } from '@/object-record/record-table/virtualization/states/dataPagesLoadedComponentState'; -import { lastScrollPositionComponentState } from '@/object-record/record-table/virtualization/states/lastScrollPositionComponentState'; -import { recordIdByRealIndexComponentFamilyState } from '@/object-record/record-table/virtualization/states/recordIdByRealIndexComponentFamilyState'; 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 { useRecoilComponentFamilyCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyCallbackState'; -import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue'; import { useCallback } from 'react'; import { useRecoilCallback } from 'recoil'; import { sleep } from '~/utils/sleep'; @@ -26,7 +19,6 @@ export const useResetVirtualizationBecauseDataChanged = ( }); const params = useFindManyRecordIndexTableParams(objectNameSingular); - const { scrollWrapperHTMLElement } = useScrollWrapperHTMLElement(); // TODO: we could optimize this by using an aggregate or using only id: true in recordGqlFields const recordGqlFields = useRecordsFieldVisibleGqlFields({ @@ -49,82 +41,20 @@ export const useResetVirtualizationBecauseDataChanged = ( dataPagesLoadedComponentState, ); - const lastScrollPositionCallbackState = useRecoilComponentCallbackState( - lastScrollPositionComponentState, - ); - - const recordIdByRealIndexCallbackState = - useRecoilComponentFamilyCallbackState( - recordIdByRealIndexComponentFamilyState, - ); - - const dataLoadingStatusByRealIndexCallbackState = - useRecoilComponentCallbackState( - dataLoadingStatusByRealIndexComponentFamilyState, - ); - const { triggerFetchPagesWithoutDebounce } = useTriggerFetchPages(); const resetVirtualization = useRecoilCallback( - ({ set, snapshot }) => + ({ set }) => 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, - ); - - for (let i = 0; i < totalNumberOfRecordsToVirtualize; i++) { - const indexIsInOverscanWindow = - i >= firstRealIndexInOverscanWindow && - i <= lastRealIndexInOverscanWindow; - - if (!indexIsInOverscanWindow) { - set( - dataLoadingStatusByRealIndexCallbackState({ - realIndex: i, - }), - null, - ); - - set( - recordIdByRealIndexCallbackState({ - realIndex: i, - }), - null, - ); - } - } set(dataPagesLoadedCallbackState, []); set(totalNumberOfRecordsToVirtualizeCallbackState, totalCount); }, [ - findManyRecordsLazy, - scrollWrapperHTMLElement?.clientHeight, - lastScrollPositionCallbackState, - totalNumberOfRecordsToVirtualizeCallbackState, dataPagesLoadedCallbackState, - dataLoadingStatusByRealIndexCallbackState, - recordIdByRealIndexCallbackState, + totalNumberOfRecordsToVirtualizeCallbackState, + findManyRecordsLazy, ], );