@@ -320,49 +320,61 @@ export function useInfiniteTableRows({
320320 useEffect ( ( ) => {
321321 if ( ! enabled || ! workspaceId || ! tableId ) return
322322 let cancelled = false
323+ let timeoutId : ReturnType < typeof setTimeout > | null = null
323324 const tick = async ( ) => {
324325 if ( cancelled ) return
325- if ( queryClient . isMutating ( ) > 0 ) return
326- const data = queryClient . getQueryData < InfiniteData < TableRowsResponse , number > > ( queryKey )
327- if ( ! data ) return
328- const dirty : number [ ] = [ ]
329- for ( let i = 0 ; i < data . pages . length ; i ++ ) {
330- if ( hasRunningGroupExecution ( data . pages [ i ] . rows ) ) {
331- dirty . push ( data . pageParams [ i ] ?? i * pageSize )
326+ if ( queryClient . isMutating ( ) === 0 ) {
327+ const data = queryClient . getQueryData < InfiniteData < TableRowsResponse , number > > ( queryKey )
328+ const dirty : number [ ] = [ ]
329+ if ( data ) {
330+ for ( let i = 0 ; i < data . pages . length ; i ++ ) {
331+ if ( hasRunningGroupExecution ( data . pages [ i ] . rows ) ) {
332+ dirty . push ( data . pageParams [ i ] ?? i * pageSize )
333+ }
334+ }
332335 }
333- }
334- if ( dirty . length === 0 ) return
335- await Promise . all (
336- dirty . map ( async ( offset ) => {
337- try {
338- const fresh = await fetchTableRows ( {
339- workspaceId,
340- tableId,
341- limit : pageSize ,
342- offset,
343- filter,
344- sort,
345- includeTotal : offset === 0 ,
336+ if ( dirty . length > 0 ) {
337+ await Promise . all (
338+ dirty . map ( async ( offset ) => {
339+ try {
340+ const fresh = await fetchTableRows ( {
341+ workspaceId,
342+ tableId,
343+ limit : pageSize ,
344+ offset,
345+ filter,
346+ sort,
347+ includeTotal : offset === 0 ,
348+ } )
349+ if ( cancelled ) return
350+ queryClient . setQueryData < InfiniteData < TableRowsResponse , number > > (
351+ queryKey ,
352+ ( prev ) => {
353+ if ( ! prev ) return prev
354+ const idx = prev . pageParams . indexOf ( offset )
355+ if ( idx === - 1 ) return prev
356+ const nextPages = prev . pages . slice ( )
357+ nextPages [ idx ] = fresh
358+ return { ...prev , pages : nextPages }
359+ }
360+ )
361+ } catch {
362+ // Transient fetch failure — next tick retries. Don't kill the loop.
363+ }
346364 } )
347- if ( cancelled ) return
348- queryClient . setQueryData < InfiniteData < TableRowsResponse , number > > ( queryKey , ( prev ) => {
349- if ( ! prev ) return prev
350- const idx = prev . pageParams . indexOf ( offset )
351- if ( idx === - 1 ) return prev
352- const nextPages = prev . pages . slice ( )
353- nextPages [ idx ] = fresh
354- return { ...prev , pages : nextPages }
355- } )
356- } catch {
357- // Transient fetch failure — next tick retries. Don't kill the loop.
358- }
359- } )
360- )
365+ )
366+ }
367+ }
368+ if ( cancelled ) return
369+ // Recursive setTimeout instead of setInterval so a slow tick can't
370+ // overlap the next one — out-of-order responses would otherwise let
371+ // stale data overwrite fresh.
372+ timeoutId = setTimeout ( ( ) => void tick ( ) , ROWS_POLL_INTERVAL_WHILE_RUNNING_MS )
361373 }
362- const intervalId = setInterval ( ( ) => void tick ( ) , ROWS_POLL_INTERVAL_WHILE_RUNNING_MS )
374+ timeoutId = setTimeout ( ( ) => void tick ( ) , ROWS_POLL_INTERVAL_WHILE_RUNNING_MS )
363375 return ( ) => {
364376 cancelled = true
365- clearInterval ( intervalId )
377+ if ( timeoutId !== null ) clearTimeout ( timeoutId )
366378 }
367379 } , [ enabled , workspaceId , tableId , pageSize , filter , sort , queryClient , queryKey ] )
368380
0 commit comments