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:
@ -4,41 +4,54 @@ import { RecoilRoot, useSetRecoilState } from 'recoil';
|
||||
import { currentUserState } from '@/auth/states/currentUserState';
|
||||
import { useDefaultHomePagePath } from '@/navigation/hooks/useDefaultHomePagePath';
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { usePrefetchedData } from '@/prefetch/hooks/usePrefetchedData';
|
||||
import { AGGREGATE_OPERATIONS } from '@/object-record/record-table/constants/AggregateOperations';
|
||||
import { prefetchViewsState } from '@/prefetch/states/prefetchViewsState';
|
||||
import { AppPath } from '@/types/AppPath';
|
||||
import { ViewType } from '@/views/types/ViewType';
|
||||
import { getCompanyObjectMetadataItem } from '~/testing/mock-data/companies';
|
||||
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
|
||||
import { mockedUserData } from '~/testing/mock-data/users';
|
||||
|
||||
jest.mock('@/prefetch/hooks/usePrefetchedData');
|
||||
const setupMockPrefetchedData = (viewId?: string) => {
|
||||
const companyObjectMetadata = generatedMockObjectMetadataItems.find(
|
||||
(item) => item.nameSingular === 'company',
|
||||
);
|
||||
|
||||
jest.mocked(usePrefetchedData).mockReturnValue({
|
||||
isDataPrefetched: true,
|
||||
records: viewId
|
||||
? [
|
||||
{
|
||||
id: viewId,
|
||||
__typename: 'object',
|
||||
objectMetadataId: companyObjectMetadata?.id,
|
||||
},
|
||||
]
|
||||
: [],
|
||||
});
|
||||
};
|
||||
|
||||
const renderHooks = (withCurrentUser: boolean) => {
|
||||
const renderHooks = ({
|
||||
withCurrentUser,
|
||||
withExistingView,
|
||||
}: {
|
||||
withCurrentUser: boolean;
|
||||
withExistingView: boolean;
|
||||
}) => {
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const setCurrentUser = useSetRecoilState(currentUserState);
|
||||
const setObjectMetadataItems = useSetRecoilState(
|
||||
objectMetadataItemsState,
|
||||
);
|
||||
const setPrefetchViews = useSetRecoilState(prefetchViewsState);
|
||||
|
||||
setObjectMetadataItems(generatedMockObjectMetadataItems);
|
||||
|
||||
if (withExistingView) {
|
||||
setPrefetchViews([
|
||||
{
|
||||
id: 'viewId',
|
||||
name: 'Test View',
|
||||
objectMetadataId: getCompanyObjectMetadataItem().id,
|
||||
type: ViewType.Table,
|
||||
key: null,
|
||||
isCompact: false,
|
||||
viewFields: [],
|
||||
viewGroups: [],
|
||||
viewSorts: [],
|
||||
kanbanFieldMetadataId: '',
|
||||
kanbanAggregateOperation: AGGREGATE_OPERATIONS.count,
|
||||
icon: '',
|
||||
kanbanAggregateOperationFieldMetadataId: '',
|
||||
position: 0,
|
||||
viewFilters: [],
|
||||
__typename: 'View',
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
if (withCurrentUser) {
|
||||
setCurrentUser(mockedUserData);
|
||||
}
|
||||
@ -52,23 +65,31 @@ const renderHooks = (withCurrentUser: boolean) => {
|
||||
};
|
||||
describe('useDefaultHomePagePath', () => {
|
||||
it('should return proper path when no currentUser', () => {
|
||||
setupMockPrefetchedData();
|
||||
const { result } = renderHooks(false);
|
||||
const { result } = renderHooks({
|
||||
withCurrentUser: false,
|
||||
withExistingView: false,
|
||||
});
|
||||
expect(result.current.defaultHomePagePath).toEqual(AppPath.SignInUp);
|
||||
});
|
||||
it('should return proper path when no currentUser and existing view', () => {
|
||||
setupMockPrefetchedData('viewId');
|
||||
const { result } = renderHooks(false);
|
||||
const { result } = renderHooks({
|
||||
withCurrentUser: false,
|
||||
withExistingView: true,
|
||||
});
|
||||
expect(result.current.defaultHomePagePath).toEqual(AppPath.SignInUp);
|
||||
});
|
||||
it('should return proper path when currentUser is defined', () => {
|
||||
setupMockPrefetchedData();
|
||||
const { result } = renderHooks(true);
|
||||
const { result } = renderHooks({
|
||||
withCurrentUser: true,
|
||||
withExistingView: false,
|
||||
});
|
||||
expect(result.current.defaultHomePagePath).toEqual('/objects/companies');
|
||||
});
|
||||
it('should return proper path when currentUser is defined and view exists', () => {
|
||||
setupMockPrefetchedData('viewId');
|
||||
const { result } = renderHooks(true);
|
||||
const { result } = renderHooks({
|
||||
withCurrentUser: true,
|
||||
withExistingView: true,
|
||||
});
|
||||
expect(result.current.defaultHomePagePath).toEqual(
|
||||
'/objects/companies?viewId=viewId',
|
||||
);
|
||||
|
||||
@ -2,10 +2,8 @@ import { currentUserState } from '@/auth/states/currentUserState';
|
||||
import { lastVisitedObjectMetadataItemIdState } from '@/navigation/states/lastVisitedObjectMetadataItemIdState';
|
||||
import { ObjectPathInfo } from '@/navigation/types/ObjectPathInfo';
|
||||
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
|
||||
import { usePrefetchedData } from '@/prefetch/hooks/usePrefetchedData';
|
||||
import { PrefetchKey } from '@/prefetch/types/PrefetchKey';
|
||||
import { prefetchViewsState } from '@/prefetch/states/prefetchViewsState';
|
||||
import { AppPath } from '@/types/AppPath';
|
||||
import { View } from '@/views/types/View';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared';
|
||||
@ -15,7 +13,7 @@ export const useDefaultHomePagePath = () => {
|
||||
const currentUser = useRecoilValue(currentUserState);
|
||||
const { activeObjectMetadataItems, alphaSortedActiveObjectMetadataItems } =
|
||||
useFilteredObjectMetadataItems();
|
||||
const { records: views } = usePrefetchedData<View>(PrefetchKey.AllViews);
|
||||
const prefetchViews = useRecoilValue(prefetchViewsState);
|
||||
const lastVisitedObjectMetadataItemId = useRecoilValue(
|
||||
lastVisitedObjectMetadataItemIdState,
|
||||
);
|
||||
@ -31,8 +29,10 @@ export const useDefaultHomePagePath = () => {
|
||||
|
||||
const getFirstView = useCallback(
|
||||
(objectMetadataItemId: string | undefined | null) =>
|
||||
views.find((view) => view.objectMetadataId === objectMetadataItemId),
|
||||
[views],
|
||||
prefetchViews.find(
|
||||
(view) => view.objectMetadataId === objectMetadataItemId,
|
||||
),
|
||||
[prefetchViews],
|
||||
);
|
||||
|
||||
const firstObjectPathInfo = useMemo<ObjectPathInfo | null>(() => {
|
||||
|
||||
@ -1,16 +1,11 @@
|
||||
import { lastVisitedViewPerObjectMetadataItemState } from '@/navigation/states/lastVisitedViewPerObjectMetadataItemState';
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { useLazyPrefetchedData } from '@/prefetch/hooks/useLazyPrefetchData';
|
||||
import { PrefetchKey } from '@/prefetch/types/PrefetchKey';
|
||||
import { prefetchViewsState } from '@/prefetch/states/prefetchViewsState';
|
||||
import { View } from '@/views/types/View';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared';
|
||||
|
||||
export const useSetLastVisitedViewForObjectMetadataNamePlural = () => {
|
||||
const { records: views, findManyRecords } = useLazyPrefetchedData<View>(
|
||||
PrefetchKey.AllViews,
|
||||
);
|
||||
|
||||
const setLastVisitedViewForObjectMetadataNamePlural = useRecoilCallback(
|
||||
({ set, snapshot }) =>
|
||||
async ({
|
||||
@ -20,7 +15,7 @@ export const useSetLastVisitedViewForObjectMetadataNamePlural = () => {
|
||||
objectNamePlural: string;
|
||||
viewId: string;
|
||||
}) => {
|
||||
await findManyRecords();
|
||||
const views = snapshot.getLoadable(prefetchViewsState).getValue();
|
||||
|
||||
const view = views.find((view: View) => view.id === viewId);
|
||||
|
||||
@ -54,7 +49,7 @@ export const useSetLastVisitedViewForObjectMetadataNamePlural = () => {
|
||||
});
|
||||
}
|
||||
},
|
||||
[findManyRecords, views],
|
||||
[],
|
||||
);
|
||||
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user