Refacto views (#10272)

In this huge (sorry!) PR:
- introducing objectMetadataItem in contextStore instead of
objectMetadataId which is more convenient
- splitting some big hooks into smaller parts to avoid re-renders
- removing Effects to avoid re-renders (especially onViewChange)
- making the view prefetch separate from favorites to avoid re-renders
- making the view prefetch load a state and add selectors on top of it
to avoir re-renders

As a result, the performance is WAY better (I suspect the favorite
implementation to trigger a lot of re-renders unfortunately).
However, we are still facing a random app freeze on view creation. I
could not investigate the root cause. As this seems to be already there
in the precedent release, we can move forward but this seems a urgent
follow up to me ==> EDIT: I've found the root cause after a few ours of
deep dive... an infinite loop in RecordTableNoRecordGroupBodyEffect...

prastoin edit: close https://github.com/twentyhq/twenty/issues/10253

---------

Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
Co-authored-by: prastoin <paul@twenty.com>
This commit is contained in:
Charles Bochet
2025-02-18 13:51:07 +01:00
committed by GitHub
parent 103dff4bd0
commit fb42046033
125 changed files with 1607 additions and 1582 deletions

View File

@ -1,9 +1,10 @@
import { useEffect } from 'react';
import { useContextStoreObjectMetadataItemOrThrow } from '@/context-store/hooks/useContextStoreObjectMetadataItemOrThrow';
import { prefetchViewsFromObjectMetadataItemFamilySelector } from '@/prefetch/states/selector/prefetchViewsFromObjectMetadataItemFamilySelector';
import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
import { useGetCurrentView } from '@/views/hooks/useGetCurrentView';
import { ViewType } from '@/views/types/ViewType';
import { useGetAvailableFieldsForKanban } from '@/views/view-picker/hooks/useGetAvailableFieldsForKanban';
import { useViewPickerMode } from '@/views/view-picker/hooks/useViewPickerMode';
@ -14,6 +15,7 @@ import { viewPickerKanbanFieldMetadataIdComponentState } from '@/views/view-pick
import { viewPickerReferenceViewIdComponentState } from '@/views/view-picker/states/viewPickerReferenceViewIdComponentState';
import { viewPickerSelectedIconComponentState } from '@/views/view-picker/states/viewPickerSelectedIconComponentState';
import { viewPickerTypeComponentState } from '@/views/view-picker/states/viewPickerTypeComponentState';
import { useRecoilValue } from 'recoil';
import { isDefined } from 'twenty-shared';
export const ViewPickerContentEffect = () => {
@ -44,7 +46,13 @@ export const ViewPickerContentEffect = () => {
viewPickerIsPersistingComponentState,
);
const { viewsOnCurrentObject } = useGetCurrentView();
const { objectMetadataItem } = useContextStoreObjectMetadataItemOrThrow();
const viewsOnCurrentObject = useRecoilValue(
prefetchViewsFromObjectMetadataItemFamilySelector({
objectMetadataItemId: objectMetadataItem.id,
}),
);
const referenceView = viewsOnCurrentObject.find(
(view) => view.id === viewPickerReferenceViewId,
);

View File

@ -1,21 +1,23 @@
import { FavoriteFolderPicker } from '@/favorites/favorite-folder-picker/components/FavoriteFolderPicker';
import { FavoriteFolderPickerEffect } from '@/favorites/favorite-folder-picker/components/FavoriteFolderPickerEffect';
import { FavoriteFolderPickerComponentInstanceContext } from '@/favorites/favorite-folder-picker/scopes/FavoriteFolderPickerScope';
import { usePrefetchedData } from '@/prefetch/hooks/usePrefetchedData';
import { PrefetchKey } from '@/prefetch/types/PrefetchKey';
import { prefetchViewFromViewIdFamilySelector } from '@/prefetch/states/selector/prefetchViewFromViewIdFamilySelector';
import { DropdownScope } from '@/ui/layout/dropdown/scopes/DropdownScope';
import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2';
import { View } from '@/views/types/View';
import { VIEW_PICKER_DROPDOWN_ID } from '@/views/view-picker/constants/ViewPickerDropdownId';
import { viewPickerReferenceViewIdComponentState } from '@/views/view-picker/states/viewPickerReferenceViewIdComponentState';
import { useRecoilValue } from 'recoil';
export const ViewPickerFavoriteFoldersDropdown = () => {
const { records: views } = usePrefetchedData<View>(PrefetchKey.AllViews);
const [viewPickerReferenceViewId] = useRecoilComponentStateV2(
viewPickerReferenceViewIdComponentState,
);
const view = views.find((view) => view.id === viewPickerReferenceViewId);
const view = useRecoilValue(
prefetchViewFromViewIdFamilySelector({
viewId: viewPickerReferenceViewId ?? '',
}),
);
return (
<FavoriteFolderPickerComponentInstanceContext

View File

@ -24,6 +24,7 @@ const StyledBoldDropdownMenuItemsContainer = styled(DropdownMenuItemsContainer)`
export const ViewPickerListContent = () => {
const { currentViewWithCombinedFiltersAndSorts, viewsOnCurrentObject } =
useGetCurrentView();
const setViewPickerReferenceViewId = useSetRecoilComponentStateV2(
viewPickerReferenceViewIdComponentState,
);

View File

@ -1,45 +1,31 @@
import { useCallback } from 'react';
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { useDropdownV2 } from '@/ui/layout/dropdown/hooks/useDropdownV2';
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
import { VIEW_PICKER_DROPDOWN_ID } from '@/views/view-picker/constants/ViewPickerDropdownId';
import { VIEW_PICKER_KANBAN_FIELD_DROPDOWN_ID } from '@/views/view-picker/constants/ViewPickerKanbanFieldDropdownId';
import { VIEW_PICKER_VIEW_TYPE_DROPDOWN_ID } from '@/views/view-picker/constants/ViewPickerViewTypeDropdownId';
import { useViewPickerMode } from '@/views/view-picker/hooks/useViewPickerMode';
import { viewPickerIsPersistingComponentState } from '@/views/view-picker/states/viewPickerIsPersistingComponentState';
import { viewPickerModeComponentState } from '@/views/view-picker/states/viewPickerModeComponentState';
export const useCloseAndResetViewPicker = () => {
const { setViewPickerMode } = useViewPickerMode();
const setViewPickerMode = useSetRecoilComponentStateV2(
viewPickerModeComponentState,
);
const setViewPickerIsPersisting = useSetRecoilComponentStateV2(
viewPickerIsPersistingComponentState,
);
const { closeDropdown: closeViewPickerDropdown } = useDropdown(
VIEW_PICKER_DROPDOWN_ID,
);
const { closeDropdown: closeKanbanFieldDropdown } = useDropdown(
VIEW_PICKER_KANBAN_FIELD_DROPDOWN_ID,
);
const { closeDropdown: closeTypeDropdown } = useDropdown(
VIEW_PICKER_VIEW_TYPE_DROPDOWN_ID,
);
const { closeDropdown } = useDropdownV2();
const closeAndResetViewPicker = useCallback(() => {
setViewPickerIsPersisting(false);
setViewPickerMode('list');
closeKanbanFieldDropdown();
closeTypeDropdown();
closeViewPickerDropdown();
}, [
closeKanbanFieldDropdown,
closeTypeDropdown,
closeViewPickerDropdown,
setViewPickerIsPersisting,
setViewPickerMode,
]);
closeDropdown(VIEW_PICKER_KANBAN_FIELD_DROPDOWN_ID);
closeDropdown(VIEW_PICKER_VIEW_TYPE_DROPDOWN_ID);
closeDropdown(VIEW_PICKER_DROPDOWN_ID);
}, [closeDropdown, setViewPickerIsPersisting, setViewPickerMode]);
return { closeAndResetViewPicker };
};