Fetch load more on group by (#18811)

Fixes https://github.com/twentyhq/twenty/issues/18587 "Load More" in
grouped record table view (aggregated view) which was broken — clicking
it loaded no records and made the button disappear

Root cause: the Load More button called fetchMore on a useLazyQuery that
was never executed, causing Apollo to throw Invariant Violation:
'fetchMore' cannot be called before executing the query. The component
now uses fetchMoreRecords from the same useQuery that powers the table
data.

Before

https://github.com/user-attachments/assets/5266424b-7f35-4261-b7b7-9f7bc3eb8ad6

After

https://github.com/user-attachments/assets/866fe7d7-720a-40c7-bb28-267b85bd98d5

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
Thomas Trompette
2026-03-22 12:59:09 +01:00
committed by GitHub
parent 9a306ddb9a
commit bdaff0b7e2
5 changed files with 21 additions and 15 deletions
@@ -3,7 +3,7 @@ import { useLazyFindManyRecords } from '@/object-record/hooks/useLazyFindManyRec
import { useRelevantRecordsGqlFields } from '@/object-record/record-field/hooks/useRelevantRecordsGqlFields';
import { useFindManyRecordIndexTableParams } from '@/object-record/record-index/hooks/useFindManyRecordIndexTableParams';
export const useRecordIndexTableFetchMore = (objectNameSingular: string) => {
export const useRecordIndexTableLazyQuery = (objectNameSingular: string) => {
const params = useFindManyRecordIndexTableParams(objectNameSingular);
const { objectMetadataItem } = useObjectMetadataItem({
@@ -18,12 +18,18 @@ export const useRecordIndexTableQuery = (objectNameSingular: string) => {
objectMetadataItem,
});
const { records, hasNextPage, queryIdentifier, loading, totalCount } =
useFindManyRecords({
...params,
recordGqlFields,
skip: showAuthModal,
});
const {
records,
hasNextPage,
queryIdentifier,
loading,
totalCount,
fetchMoreRecords,
} = useFindManyRecords({
...params,
recordGqlFields,
skip: showAuthModal,
});
return {
records: showAuthModal ? SIGN_IN_BACKGROUND_MOCK_COMPANIES : records,
@@ -31,5 +37,6 @@ export const useRecordIndexTableQuery = (objectNameSingular: string) => {
hasNextPage,
queryIdentifier,
totalCount,
fetchMoreRecords,
};
};
@@ -1,5 +1,5 @@
import { useCurrentRecordGroupId } from '@/object-record/record-group/hooks/useCurrentRecordGroupId';
import { useRecordIndexTableFetchMore } from '@/object-record/record-index/hooks/useRecordIndexTableFetchMore';
import { useRecordIndexTableQuery } from '@/object-record/record-index/hooks/useRecordIndexTableQuery';
import { recordIndexHasFetchedAllRecordsByGroupComponentState } from '@/object-record/record-index/states/recordIndexHasFetchedAllRecordsByGroupComponentState';
import { useRecordTableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableContext';
import { RecordTableActionRow } from '@/object-record/record-table/record-table-row/components/RecordTableActionRow';
@@ -12,8 +12,7 @@ export const RecordTableRecordGroupSectionLoadMore = () => {
const currentRecordGroupId = useCurrentRecordGroupId();
const { fetchMoreRecordsLazy } =
useRecordIndexTableFetchMore(objectNameSingular);
const { fetchMoreRecords } = useRecordIndexTableQuery(objectNameSingular);
const recordIndexHasFetchedAllRecordsByGroup =
useAtomComponentFamilyStateValue(
@@ -22,7 +21,7 @@ export const RecordTableRecordGroupSectionLoadMore = () => {
);
const handleLoadMore = () => {
fetchMoreRecordsLazy();
fetchMoreRecords();
};
if (recordIndexHasFetchedAllRecordsByGroup) {
@@ -1,4 +1,4 @@
import { useRecordIndexTableFetchMore } from '@/object-record/record-index/hooks/useRecordIndexTableFetchMore';
import { useRecordIndexTableLazyQuery } from '@/object-record/record-index/hooks/useRecordIndexTableLazyQuery';
import { useRecordTableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableContext';
import { visibleRecordFieldsComponentSelector } from '@/object-record/record-field/states/visibleRecordFieldsComponentSelector';
@@ -20,7 +20,7 @@ import { useEffect } from 'react';
export const RecordTableVirtualizedInitialDataLoadEffect = () => {
const { recordTableId, objectNameSingular } = useRecordTableContextOrThrow();
const { queryIdentifier } = useRecordIndexTableFetchMore(objectNameSingular);
const { queryIdentifier } = useRecordIndexTableLazyQuery(objectNameSingular);
const [lastRecordTableQueryIdentifier, setLastRecordTableQueryIdentifier] =
useAtomComponentState(lastRecordTableQueryIdentifierComponentState);
@@ -1,7 +1,7 @@
import { useCallback } from 'react';
import { useStore } from 'jotai';
import { useRecordIndexTableFetchMore } from '@/object-record/record-index/hooks/useRecordIndexTableFetchMore';
import { useRecordIndexTableLazyQuery } from '@/object-record/record-index/hooks/useRecordIndexTableLazyQuery';
import { recordIndexAllRecordIdsComponentSelector } from '@/object-record/record-index/states/selectors/recordIndexAllRecordIdsComponentSelector';
import { useUpsertRecordsInStore } from '@/object-record/record-store/hooks/useUpsertRecordsInStore';
import { RECORD_TABLE_HORIZONTAL_SCROLL_SHADOW_VISIBILITY_CSS_VARIABLE_NAME } from '@/object-record/record-table/constants/RecordTableHorizontalScrollShadowVisibilityCssVariableName';
@@ -39,7 +39,7 @@ export const useTriggerInitialRecordTableDataLoad = () => {
const showAuthModal = useShowAuthModal();
const { findManyRecordsLazy } =
useRecordIndexTableFetchMore(objectNameSingular);
useRecordIndexTableLazyQuery(objectNameSingular);
const isInitializingVirtualTableDataLoadingCallbackState =
useAtomComponentStateCallbackState(