fix: handle Apollo v4 useLazyQuery rejection to prevent permanent loading state
In Apollo v4, useLazyQuery's execute function returns a ResultPromise that can reject on errors (unlike v3 where it resolved with the error in the result). This caused the first object's list view to stay in permanent loading after page refresh, because the unhandled rejection left isRecordTableInitialLoading and isInitializingVirtualTableDataLoading stuck in their initial states. Two fixes: - Add try/catch in findManyRecordsLazy to gracefully handle execute rejections (matching v3 behavior of returning errors in the result) - Add try/finally in triggerInitialRecordTableDataLoad to ensure loading states are always cleaned up regardless of errors https://claude.ai/code/session_01EA7CMTUTG5zFP132yvUweT
This commit is contained in:
+5
-1
@@ -47,7 +47,11 @@ export const useFindDuplicateRecordsQuery = ({
|
||||
}
|
||||
}
|
||||
`,
|
||||
[objectMetadataItem, objectMetadataItems, objectPermissionsByObjectMetadataId],
|
||||
[
|
||||
objectMetadataItem,
|
||||
objectMetadataItems,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
],
|
||||
);
|
||||
|
||||
return {
|
||||
|
||||
@@ -4,6 +4,7 @@ import { useCallback, useMemo } from 'react';
|
||||
import { useApolloCoreClient } from '@/object-metadata/hooks/useApolloCoreClient';
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { getRecordsFromRecordConnection } from '@/object-record/cache/utils/getRecordsFromRecordConnection';
|
||||
import { type ErrorLike } from '@apollo/client';
|
||||
import { type RecordGqlOperationFindManyResult } from '@/object-record/graphql/types/RecordGqlOperationFindManyResult';
|
||||
import { type UseFindManyRecordsParams } from '@/object-record/hooks/useFindManyRecords';
|
||||
import { useFindManyRecordsQuery } from '@/object-record/hooks/useFindManyRecordsQuery';
|
||||
@@ -100,7 +101,28 @@ export const useLazyFindManyRecords = <T extends ObjectRecord = ObjectRecord>({
|
||||
};
|
||||
}
|
||||
|
||||
const result = await findManyRecords({ variables: defaultVariables });
|
||||
// In Apollo v4, useLazyQuery's execute can reject (unlike v3 which
|
||||
// resolved with the error in the result). We catch rejections here to
|
||||
// prevent unhandled exceptions that would leave loading states stuck.
|
||||
let result;
|
||||
|
||||
try {
|
||||
result = await findManyRecords({ variables: defaultVariables });
|
||||
} catch (executionError) {
|
||||
handleFindManyRecordsError(executionError as ErrorLike);
|
||||
|
||||
store.set(hasNextPageFamilyState.atomFamily(queryIdentifier), false);
|
||||
store.set(cursorFamilyState.atomFamily(queryIdentifier), '');
|
||||
|
||||
return {
|
||||
data: null,
|
||||
records: [] as T[],
|
||||
totalCount: 0,
|
||||
hasNextPage: false,
|
||||
error: executionError as ErrorLike,
|
||||
};
|
||||
}
|
||||
|
||||
if (isDefined(result?.error)) {
|
||||
handleFindManyRecordsError(result.error);
|
||||
}
|
||||
|
||||
+68
-66
@@ -121,85 +121,87 @@ export const useTriggerInitialRecordTableDataLoad = () => {
|
||||
|
||||
store.set(isInitializingVirtualTableDataLoadingCallbackState, true);
|
||||
|
||||
resetTableFocuses();
|
||||
try {
|
||||
resetTableFocuses();
|
||||
|
||||
resetVirtualizedRowTreadmill();
|
||||
resetVirtualizedRowTreadmill();
|
||||
|
||||
updateRecordTableCSSVariable(
|
||||
RECORD_TABLE_VERTICAL_SCROLL_SHADOW_VISIBILITY_CSS_VARIABLE_NAME,
|
||||
'hidden',
|
||||
);
|
||||
|
||||
updateRecordTableCSSVariable(
|
||||
RECORD_TABLE_HORIZONTAL_SCROLL_SHADOW_VISIBILITY_CSS_VARIABLE_NAME,
|
||||
'hidden',
|
||||
);
|
||||
|
||||
const currentRecordIds = store.get(recordIndexAllRecordIds);
|
||||
|
||||
let records: ObjectRecord[] | null = null;
|
||||
let totalCount = 0;
|
||||
|
||||
if (showAuthModal) {
|
||||
records = SIGN_IN_BACKGROUND_MOCK_COMPANIES;
|
||||
totalCount = SIGN_IN_BACKGROUND_MOCK_COMPANIES.length;
|
||||
} else {
|
||||
const newRecordIdByRealIndex = new Map(
|
||||
store.get(recordIdByRealIndexCallbackState),
|
||||
);
|
||||
const newDataLoadingStatusByRealIndex = new Map(
|
||||
store.get(dataLoadingStatusByRealIndexCallbackState),
|
||||
updateRecordTableCSSVariable(
|
||||
RECORD_TABLE_VERTICAL_SCROLL_SHADOW_VISIBILITY_CSS_VARIABLE_NAME,
|
||||
'hidden',
|
||||
);
|
||||
|
||||
for (const [realIndex] of currentRecordIds.entries()) {
|
||||
newDataLoadingStatusByRealIndex.set(realIndex, 'not-loaded');
|
||||
newRecordIdByRealIndex.delete(realIndex);
|
||||
updateRecordTableCSSVariable(
|
||||
RECORD_TABLE_HORIZONTAL_SCROLL_SHADOW_VISIBILITY_CSS_VARIABLE_NAME,
|
||||
'hidden',
|
||||
);
|
||||
|
||||
const currentRecordIds = store.get(recordIndexAllRecordIds);
|
||||
|
||||
let records: ObjectRecord[] | null = null;
|
||||
let totalCount = 0;
|
||||
|
||||
if (showAuthModal) {
|
||||
records = SIGN_IN_BACKGROUND_MOCK_COMPANIES;
|
||||
totalCount = SIGN_IN_BACKGROUND_MOCK_COMPANIES.length;
|
||||
} else {
|
||||
const newRecordIdByRealIndex = new Map(
|
||||
store.get(recordIdByRealIndexCallbackState),
|
||||
);
|
||||
const newDataLoadingStatusByRealIndex = new Map(
|
||||
store.get(dataLoadingStatusByRealIndexCallbackState),
|
||||
);
|
||||
|
||||
for (const [realIndex] of currentRecordIds.entries()) {
|
||||
newDataLoadingStatusByRealIndex.set(realIndex, 'not-loaded');
|
||||
newRecordIdByRealIndex.delete(realIndex);
|
||||
}
|
||||
|
||||
store.set(recordIdByRealIndexCallbackState, newRecordIdByRealIndex);
|
||||
store.set(
|
||||
dataLoadingStatusByRealIndexCallbackState,
|
||||
newDataLoadingStatusByRealIndex,
|
||||
);
|
||||
|
||||
const { records: findManyRecords, totalCount: findManyTotalCount } =
|
||||
await findManyRecordsLazy();
|
||||
|
||||
records = findManyRecords;
|
||||
totalCount = findManyTotalCount;
|
||||
}
|
||||
|
||||
store.set(recordIdByRealIndexCallbackState, newRecordIdByRealIndex);
|
||||
store.set(
|
||||
dataLoadingStatusByRealIndexCallbackState,
|
||||
newDataLoadingStatusByRealIndex,
|
||||
);
|
||||
store.set(totalNumberOfRecordsToVirtualizeCallbackState, totalCount);
|
||||
|
||||
const { records: findManyRecords, totalCount: findManyTotalCount } =
|
||||
await findManyRecordsLazy();
|
||||
if (isDefined(records)) {
|
||||
upsertRecordsInStore({ partialRecords: records });
|
||||
|
||||
records = findManyRecords;
|
||||
totalCount = findManyTotalCount;
|
||||
}
|
||||
loadRecordsToVirtualRows({
|
||||
records,
|
||||
startingRealIndex: 0,
|
||||
});
|
||||
|
||||
store.set(totalNumberOfRecordsToVirtualizeCallbackState, totalCount);
|
||||
reapplyRowSelection();
|
||||
}
|
||||
|
||||
if (isDefined(records)) {
|
||||
upsertRecordsInStore({ partialRecords: records });
|
||||
store.set(dataPagesLoadedCallbackState, []);
|
||||
|
||||
loadRecordsToVirtualRows({
|
||||
records,
|
||||
startingRealIndex: 0,
|
||||
});
|
||||
store.set(lastScrollPositionCallbackState, 0);
|
||||
store.set(lastRealIndexSetCallbackState, null);
|
||||
store.set(scrollAtRealIndexCallbackState, 0);
|
||||
|
||||
reapplyRowSelection();
|
||||
}
|
||||
setIsRecordTableScrolledHorizontally(false);
|
||||
setIsRecordTableScrolledVertically(false);
|
||||
resetTableFocuses();
|
||||
|
||||
store.set(dataPagesLoadedCallbackState, []);
|
||||
|
||||
store.set(isInitializingVirtualTableDataLoadingCallbackState, false);
|
||||
store.set(isRecordTableInitialLoading, false);
|
||||
|
||||
store.set(lastScrollPositionCallbackState, 0);
|
||||
store.set(lastRealIndexSetCallbackState, null);
|
||||
store.set(scrollAtRealIndexCallbackState, 0);
|
||||
|
||||
setIsRecordTableScrolledHorizontally(false);
|
||||
setIsRecordTableScrolledVertically(false);
|
||||
resetTableFocuses();
|
||||
|
||||
if (shouldScrollToStart) {
|
||||
scrollTableToPosition({
|
||||
horizontalScrollInPx: 0,
|
||||
verticalScrollInPx: 0,
|
||||
});
|
||||
if (shouldScrollToStart) {
|
||||
scrollTableToPosition({
|
||||
horizontalScrollInPx: 0,
|
||||
verticalScrollInPx: 0,
|
||||
});
|
||||
}
|
||||
} finally {
|
||||
store.set(isInitializingVirtualTableDataLoadingCallbackState, false);
|
||||
store.set(isRecordTableInitialLoading, false);
|
||||
}
|
||||
},
|
||||
[
|
||||
|
||||
Reference in New Issue
Block a user