Fix infinite loading on field settings (#8938)
We were experiencing infinite loading on field settings pages (creation of new field), due to the fact that the component was being rendered on and on and on. This was due to useGetCurrentUserQuery calls outside of the update function, causing renders in cascade. We also had an issue with the component being unmounted too often. --------- Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
@ -16,10 +16,12 @@ export const ObjectMetadataItemsLoadEffect = () => {
|
|||||||
const currentWorkspace = useRecoilValue(currentWorkspaceState);
|
const currentWorkspace = useRecoilValue(currentWorkspaceState);
|
||||||
const isLoggedIn = useIsLogged();
|
const isLoggedIn = useIsLogged();
|
||||||
|
|
||||||
const { objectMetadataItems: newObjectMetadataItems } =
|
const {
|
||||||
useFindManyObjectMetadataItems({
|
objectMetadataItems: newObjectMetadataItems,
|
||||||
skip: !isLoggedIn,
|
loading: isObjectMetadataLoading,
|
||||||
});
|
} = useFindManyObjectMetadataItems({
|
||||||
|
skip: !isLoggedIn,
|
||||||
|
});
|
||||||
|
|
||||||
const updateObjectMetadataItems = useRecoilCallback(
|
const updateObjectMetadataItems = useRecoilCallback(
|
||||||
({ set, snapshot }) =>
|
({ set, snapshot }) =>
|
||||||
@ -32,6 +34,7 @@ export const ObjectMetadataItemsLoadEffect = () => {
|
|||||||
: newObjectMetadataItems;
|
: newObjectMetadataItems;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
!isObjectMetadataLoading &&
|
||||||
!isDeeplyEqual(
|
!isDeeplyEqual(
|
||||||
snapshot.getLoadable(objectMetadataItemsState).getValue(),
|
snapshot.getLoadable(objectMetadataItemsState).getValue(),
|
||||||
toSetObjectMetadataItems,
|
toSetObjectMetadataItems,
|
||||||
@ -40,7 +43,12 @@ export const ObjectMetadataItemsLoadEffect = () => {
|
|||||||
set(objectMetadataItemsState, toSetObjectMetadataItems);
|
set(objectMetadataItemsState, toSetObjectMetadataItems);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[currentUser, currentWorkspace?.activationStatus, newObjectMetadataItems],
|
[
|
||||||
|
currentUser,
|
||||||
|
currentWorkspace?.activationStatus,
|
||||||
|
isObjectMetadataLoading,
|
||||||
|
newObjectMetadataItems,
|
||||||
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@ -12,9 +12,8 @@ import { FIND_MANY_OBJECT_METADATA_ITEMS } from '../graphql/queries';
|
|||||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||||
import { useFindManyRecordsQuery } from '@/object-record/hooks/useFindManyRecordsQuery';
|
import { useFindManyRecordsQuery } from '@/object-record/hooks/useFindManyRecordsQuery';
|
||||||
|
import { GET_CURRENT_USER } from '@/users/graphql/queries/getCurrentUser';
|
||||||
import { useSetRecoilState } from 'recoil';
|
import { useSetRecoilState } from 'recoil';
|
||||||
import { isDefined } from 'twenty-ui';
|
|
||||||
import { useGetCurrentUserQuery } from '~/generated/graphql';
|
|
||||||
import { useApolloMetadataClient } from './useApolloMetadataClient';
|
import { useApolloMetadataClient } from './useApolloMetadataClient';
|
||||||
|
|
||||||
export const useUpdateOneFieldMetadataItem = () => {
|
export const useUpdateOneFieldMetadataItem = () => {
|
||||||
@ -23,15 +22,7 @@ export const useUpdateOneFieldMetadataItem = () => {
|
|||||||
|
|
||||||
const setCurrentWorkspace = useSetRecoilState(currentWorkspaceState);
|
const setCurrentWorkspace = useSetRecoilState(currentWorkspaceState);
|
||||||
|
|
||||||
const { refetch: refetchCurrentUser } = useGetCurrentUserQuery({
|
const { findManyRecordsQuery: findManyViewsQuery } = useFindManyRecordsQuery({
|
||||||
onCompleted: (data) => {
|
|
||||||
if (isDefined(data?.currentUser?.defaultWorkspace)) {
|
|
||||||
setCurrentWorkspace(data.currentUser.defaultWorkspace);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const { findManyRecordsQuery } = useFindManyRecordsQuery({
|
|
||||||
objectNameSingular: CoreObjectNameSingular.View,
|
objectNameSingular: CoreObjectNameSingular.View,
|
||||||
recordGqlFields: {
|
recordGqlFields: {
|
||||||
id: true,
|
id: true,
|
||||||
@ -80,10 +71,11 @@ export const useUpdateOneFieldMetadataItem = () => {
|
|||||||
refetchQueries: [getOperationName(FIND_MANY_OBJECT_METADATA_ITEMS) ?? ''],
|
refetchQueries: [getOperationName(FIND_MANY_OBJECT_METADATA_ITEMS) ?? ''],
|
||||||
});
|
});
|
||||||
|
|
||||||
await refetchCurrentUser();
|
const { data } = await apolloClient.query({ query: GET_CURRENT_USER });
|
||||||
|
setCurrentWorkspace(data?.currentUser?.defaultWorkspace);
|
||||||
|
|
||||||
await apolloClient.query({
|
await apolloClient.query({
|
||||||
query: findManyRecordsQuery,
|
query: findManyViewsQuery,
|
||||||
variables: {
|
variables: {
|
||||||
filter: {
|
filter: {
|
||||||
objectMetadataId: {
|
objectMetadataId: {
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { WORKSPACE_MEMBER_QUERY_FRAGMENT } from '@/workspace-member/graphql/fragments/workspaceMemberQueryFragment';
|
||||||
import { gql } from '@apollo/client';
|
import { gql } from '@apollo/client';
|
||||||
|
|
||||||
export const USER_QUERY_FRAGMENT = gql`
|
export const USER_QUERY_FRAGMENT = gql`
|
||||||
@ -62,4 +63,6 @@ export const USER_QUERY_FRAGMENT = gql`
|
|||||||
}
|
}
|
||||||
userVars
|
userVars
|
||||||
}
|
}
|
||||||
|
|
||||||
|
${WORKSPACE_MEMBER_QUERY_FRAGMENT}
|
||||||
`;
|
`;
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
// This query cannot be put in the graphQL folder because it cannot be generated by the graphQL codegen.
|
// This query cannot be put in the graphQL folder because it cannot be generated by the graphQL codegen.
|
||||||
|
import { USER_QUERY_FRAGMENT } from '@/users/graphql/fragments/userQueryFragment';
|
||||||
import { gql } from '@apollo/client';
|
import { gql } from '@apollo/client';
|
||||||
|
|
||||||
export const GET_CURRENT_USER = gql`
|
export const GET_CURRENT_USER = gql`
|
||||||
@ -7,4 +8,6 @@ export const GET_CURRENT_USER = gql`
|
|||||||
...UserQueryFragment
|
...UserQueryFragment
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
${USER_QUERY_FRAGMENT}
|
||||||
`;
|
`;
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { gql } from '@apollo/client';
|
import { gql } from '@apollo/client';
|
||||||
|
|
||||||
export const USER_QUERY_FRAGMENT = gql`
|
export const WORKSPACE_MEMBER_QUERY_FRAGMENT = gql`
|
||||||
fragment WorkspaceMemberQueryFragment on WorkspaceMember {
|
fragment WorkspaceMemberQueryFragment on WorkspaceMember {
|
||||||
id
|
id
|
||||||
name {
|
name {
|
||||||
|
|||||||
@ -1,8 +1,7 @@
|
|||||||
import { useApolloClient } from '@apollo/client';
|
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
import omit from 'lodash.omit';
|
import omit from 'lodash.omit';
|
||||||
import pick from 'lodash.pick';
|
import pick from 'lodash.pick';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { FormProvider, useForm } from 'react-hook-form';
|
import { FormProvider, useForm } from 'react-hook-form';
|
||||||
import { useNavigate, useParams } from 'react-router-dom';
|
import { useNavigate, useParams } from 'react-router-dom';
|
||||||
import {
|
import {
|
||||||
@ -21,7 +20,6 @@ import { useUpdateOneFieldMetadataItem } from '@/object-metadata/hooks/useUpdate
|
|||||||
import { formatFieldMetadataItemInput } from '@/object-metadata/utils/formatFieldMetadataItemInput';
|
import { formatFieldMetadataItemInput } from '@/object-metadata/utils/formatFieldMetadataItemInput';
|
||||||
import { getFieldSlug } from '@/object-metadata/utils/getFieldSlug';
|
import { getFieldSlug } from '@/object-metadata/utils/getFieldSlug';
|
||||||
import { isLabelIdentifierField } from '@/object-metadata/utils/isLabelIdentifierField';
|
import { isLabelIdentifierField } from '@/object-metadata/utils/isLabelIdentifierField';
|
||||||
import { useFindManyRecordsQuery } from '@/object-record/hooks/useFindManyRecordsQuery';
|
|
||||||
import { RecordFieldValueSelectorContextProvider } from '@/object-record/record-store/contexts/RecordFieldValueSelectorContext';
|
import { RecordFieldValueSelectorContextProvider } from '@/object-record/record-store/contexts/RecordFieldValueSelectorContext';
|
||||||
import { SaveAndCancelButtons } from '@/settings/components/SaveAndCancelButtons/SaveAndCancelButtons';
|
import { SaveAndCancelButtons } from '@/settings/components/SaveAndCancelButtons/SaveAndCancelButtons';
|
||||||
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
|
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
|
||||||
@ -49,7 +47,6 @@ type SettingsDataModelFieldEditFormValues = z.infer<
|
|||||||
export const SettingsObjectFieldEdit = () => {
|
export const SettingsObjectFieldEdit = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { enqueueSnackBar } = useSnackBar();
|
const { enqueueSnackBar } = useSnackBar();
|
||||||
const [isPersisting, setIsPersisting] = useState(false);
|
|
||||||
|
|
||||||
const { objectSlug = '', fieldSlug = '' } = useParams();
|
const { objectSlug = '', fieldSlug = '' } = useParams();
|
||||||
const { findObjectMetadataItemBySlug } = useFilteredObjectMetadataItems();
|
const { findObjectMetadataItemBySlug } = useFilteredObjectMetadataItems();
|
||||||
@ -66,20 +63,6 @@ export const SettingsObjectFieldEdit = () => {
|
|||||||
const getRelationMetadata = useGetRelationMetadata();
|
const getRelationMetadata = useGetRelationMetadata();
|
||||||
const { updateOneFieldMetadataItem } = useUpdateOneFieldMetadataItem();
|
const { updateOneFieldMetadataItem } = useUpdateOneFieldMetadataItem();
|
||||||
|
|
||||||
const apolloClient = useApolloClient();
|
|
||||||
|
|
||||||
const { findManyRecordsQuery } = useFindManyRecordsQuery({
|
|
||||||
objectNameSingular: objectMetadataItem?.nameSingular || '',
|
|
||||||
});
|
|
||||||
|
|
||||||
const refetchRecords = async () => {
|
|
||||||
if (!objectMetadataItem) return;
|
|
||||||
await apolloClient.query({
|
|
||||||
query: findManyRecordsQuery,
|
|
||||||
fetchPolicy: 'network-only',
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const formConfig = useForm<SettingsDataModelFieldEditFormValues>({
|
const formConfig = useForm<SettingsDataModelFieldEditFormValues>({
|
||||||
mode: 'onTouched',
|
mode: 'onTouched',
|
||||||
resolver: zodResolver(settingsFieldFormSchema()),
|
resolver: zodResolver(settingsFieldFormSchema()),
|
||||||
@ -93,11 +76,10 @@ export const SettingsObjectFieldEdit = () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isPersisting) return;
|
|
||||||
if (!objectMetadataItem || !fieldMetadataItem) {
|
if (!objectMetadataItem || !fieldMetadataItem) {
|
||||||
navigate(AppPath.NotFound);
|
navigate(AppPath.NotFound);
|
||||||
}
|
}
|
||||||
}, [navigate, objectMetadataItem, fieldMetadataItem, isPersisting]);
|
}, [navigate, objectMetadataItem, fieldMetadataItem]);
|
||||||
|
|
||||||
const { isDirty, isValid, isSubmitting } = formConfig.formState;
|
const { isDirty, isValid, isSubmitting } = formConfig.formState;
|
||||||
const canSave = isDirty && isValid && !isSubmitting;
|
const canSave = isDirty && isValid && !isSubmitting;
|
||||||
@ -128,8 +110,6 @@ export const SettingsObjectFieldEdit = () => {
|
|||||||
}) ?? {};
|
}) ?? {};
|
||||||
|
|
||||||
if (isDefined(relationFieldMetadataItem)) {
|
if (isDefined(relationFieldMetadataItem)) {
|
||||||
setIsPersisting(true);
|
|
||||||
|
|
||||||
await updateOneFieldMetadataItem({
|
await updateOneFieldMetadataItem({
|
||||||
objectMetadataId: objectMetadataItem.id,
|
objectMetadataId: objectMetadataItem.id,
|
||||||
fieldMetadataIdToUpdate: relationFieldMetadataItem.id,
|
fieldMetadataIdToUpdate: relationFieldMetadataItem.id,
|
||||||
@ -146,7 +126,7 @@ export const SettingsObjectFieldEdit = () => {
|
|||||||
Object.keys(otherDirtyFields),
|
Object.keys(otherDirtyFields),
|
||||||
);
|
);
|
||||||
|
|
||||||
setIsPersisting(true);
|
navigate(`/settings/objects/${objectSlug}`);
|
||||||
|
|
||||||
await updateOneFieldMetadataItem({
|
await updateOneFieldMetadataItem({
|
||||||
objectMetadataId: objectMetadataItem.id,
|
objectMetadataId: objectMetadataItem.id,
|
||||||
@ -154,16 +134,10 @@ export const SettingsObjectFieldEdit = () => {
|
|||||||
updatePayload: formattedInput,
|
updatePayload: formattedInput,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
navigate(`/settings/objects/${objectSlug}`);
|
|
||||||
|
|
||||||
refetchRecords();
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
enqueueSnackBar((error as Error).message, {
|
enqueueSnackBar((error as Error).message, {
|
||||||
variant: SnackBarVariant.Error,
|
variant: SnackBarVariant.Error,
|
||||||
});
|
});
|
||||||
} finally {
|
|
||||||
setIsPersisting(false);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user