diff --git a/js_modules/dagster-ui/packages/ui-core/src/runs/RunsFeedRow.tsx b/js_modules/dagster-ui/packages/ui-core/src/runs/RunsFeedRow.tsx index ed96f57026bd6..a4391b600bb3b 100644 --- a/js_modules/dagster-ui/packages/ui-core/src/runs/RunsFeedRow.tsx +++ b/js_modules/dagster-ui/packages/ui-core/src/runs/RunsFeedRow.tsx @@ -105,9 +105,7 @@ export const RunsFeedRow = ({ style={{gap: '4px 8px', lineHeight: 0}} > {entry.__typename === 'PartitionBackfill' ? ( - - Backfill - + Backfill ) : undefined} (options: { query: DocumentNode; - nextCursorForResult: (result: T) => string | undefined; skip?: boolean; variables: Omit; pageSize: number; - getResultArray: (result: T | undefined) => any[]; queryKey?: string; + getResultArray: (result: T | undefined) => any[]; + nextCursorForResult: (result: T) => string | undefined; + hasMoreForResult?: (result: T) => boolean; }) { const [cursorStack, setCursorStack] = useState(() => []); const [cursor, setCursor] = useQueryPersistedState({ queryKey: options.queryKey || 'cursor', }); - const queryVars: any = { - ...options.variables, - cursor, - limit: options.pageSize + 1, - }; + // If you don't provide a hasMoreForResult function for extracting hasMore from + // the response, we fall back to an old approach that fetched one extra item + // and used it's presence to determine if more items were available. If you use + // the old approach, your `nextCursorForResult` method needs to use + // `items[pageSize - 1]` NOT `items[items.length - 1]` to get the next cursor, + // or an item will be skipped when you advance. + // + const limit = options.hasMoreForResult ? options.pageSize : options.pageSize + 1; + const queryVars: any = {...options.variables, cursor, limit}; const queryResult = useQuery(options.query, { skip: options.skip, @@ -49,9 +54,17 @@ export function useCursorPaginatedQuery { const nextStack = [...cursorStack]; setCursor(nextStack.pop()); diff --git a/js_modules/dagster-ui/packages/ui-core/src/runs/useRunsFeedEntries.tsx b/js_modules/dagster-ui/packages/ui-core/src/runs/useRunsFeedEntries.tsx index 7adaa59dd81f9..3b23ceab14bdb 100644 --- a/js_modules/dagster-ui/packages/ui-core/src/runs/useRunsFeedEntries.tsx +++ b/js_modules/dagster-ui/packages/ui-core/src/runs/useRunsFeedEntries.tsx @@ -1,3 +1,5 @@ +import {useMemo} from 'react'; + import {RUNS_FEED_TABLE_ENTRY_FRAGMENT} from './RunsFeedRow'; import {useSelectedRunsFeedTab} from './RunsFeedTabs'; import {SCHEDULED_RUNS_LIST_QUERY} from './ScheduledRunListRoot'; @@ -11,7 +13,7 @@ import {gql, useQuery} from '../apollo-client'; import {PYTHON_ERROR_FRAGMENT} from '../app/PythonErrorFragment'; import {RunsFilter} from '../graphql/types'; -const PAGE_SIZE = 29; +const PAGE_SIZE = 30; export const RUNS_FEED_CURSOR_KEY = `runs_before`; @@ -30,11 +32,17 @@ export function useRunsFeedEntries( pageSize: PAGE_SIZE, variables: {filter, includeRunsFromBackfills}, skip: isScheduled, - nextCursorForResult: (runs) => { - if (runs.runsFeedOrError.__typename !== 'RunsFeedConnection') { + nextCursorForResult: (data) => { + if (data.runsFeedOrError.__typename !== 'RunsFeedConnection') { return undefined; } - return runs.runsFeedOrError.hasMore ? runs.runsFeedOrError.cursor : undefined; + return data.runsFeedOrError.hasMore ? data.runsFeedOrError.cursor : undefined; + }, + hasMoreForResult: (data) => { + if (data.runsFeedOrError.__typename !== 'RunsFeedConnection') { + return false; + } + return data.runsFeedOrError.hasMore; }, getResultArray: (data) => { if (!data || data.runsFeedOrError.__typename !== 'RunsFeedConnection') { @@ -46,8 +54,11 @@ export function useRunsFeedEntries( const data = queryResult.data || queryResult.previousData; - const entries = - data?.runsFeedOrError.__typename === 'RunsFeedConnection' ? data?.runsFeedOrError.results : []; + const entries = useMemo(() => { + return data?.runsFeedOrError.__typename === 'RunsFeedConnection' + ? data?.runsFeedOrError.results + : []; + }, [data]); const scheduledQueryResult = useQuery( SCHEDULED_RUNS_LIST_QUERY,