query icon indicating copy to clipboard operation
query copied to clipboard

Infinite loading with useInfiniteQuery promise + React use hook in Suspense

Open BoubleJ opened this issue 3 months ago • 1 comments

Describe the bug

I'm experiencing an infinite loading issue when using useInfiniteQuery's promise with React's use hook inside a Suspense boundary. Setup:

Using useInfiniteQuery with experimental_prefetchInRender: true Returning the promise property from the query Consuming the promise with React's use() hook Component is wrapped in a <Suspense> boundary

Issue:

The page enters an infinite loading state The UI renders correctly on screen Chrome DevTools Network tab shows: "Caution: request is not finished yet" in the document request The request never completes

export default function DetailReviewWrapper({ pid, userId }: DetailReviewProps) {
  const { infoData, observerRef, isFetchingNextPage, refetch, promise } = useReviewListInfinity({
    userId,
    pid,
  })
  return (
    <Suspense fallback={<DetailReviewLoading />}>
      <DetailReview
        pid={pid}
        userId={userId}
        promise={promise}
        refetch={refetch}
        isFetchingNextPage={isFetchingNextPage}
        observerRef={observerRef}
      />
    </Suspense>
  )
}

export default function DetailReview({
  pid,
  userId,
  promise,
  refetch,
  isFetchingNextPage,
  observerRef,
}: DetailReviewProps) {

  const data = use(promise)

  return (
    <>
      <section>
 <ReviewListView
          userId={userId}
          pid={pid}
          data={data?.list || []}
          className={cn('review-list')}
          refetch={refetch}
          showPoint
        />
        {isFetchingNextPage && <Loading />}
        <div ref={observerRef} />   
   </section>


export function useReviewListInfinity(params: UseReviewListInfinityProps) {
  const section = useSearchParams().get('contentType') as SectionType
  const orderType = useRecoilValue(commentOrderState)
  const filterType = useRecoilValue(filterTypeState)

  const { fetchNextPage, hasNextPage, isFetchingNextPage, isLoading, refetch, promise } = useInfiniteQuery({
    queryKey: [REVIEW_LIST_KEY, params, orderType, filterType, section],
    queryFn: ({ pageParam = 1 }) =>
      getReviewApi({
        uid: params.userId,
        pid: params.pid,
        section,
        pageInfo: {
          curPage: pageParam,
          pageSize: 20,
        },
        orderType: orderType === 'RECOMMENDED' ? 'like' : 'date',
        filterType,
      }),
    select: (data) => {
      return {
        list: data.pages.flatMap((page) => page.data.list),
        info: data.pages[0].data.info,
      }
    },
    getNextPageParam: (lastPage) => {
      const nextPage = lastPage.data.pageInfo.curPage + 1
      return nextPage <= lastPage.data.pageInfo.pageCount ? nextPage : undefined
    },
    initialPageParam: 1,
  })

  const { observerRef } = useIntersectionObserver({
    onScroll: () => {
      if (hasNextPage && !isFetchingNextPage) {
        fetchNextPage()
      }
    },
  })

  return {
    infoData,
    isLoading,
    isFetchingNextPage,
    observerRef,
    refetch,
    promise,
  }
}


const getQueryClient = (): QueryClient =>
  new QueryClient({
    defaultOptions: {
      queries: {
        refetchOnWindowFocus: false,
        refetchInterval: false,
        refetchOnReconnect: true,
        refetchOnMount: true,
        staleTime: Infinity,
        gcTime: Infinity,
        retry: false,
        experimental_prefetchInRender: true,
      },
    },
  })

Your minimal, reproducible example

No response

Steps to reproduce

No response

Expected behavior

he page request should complete when the promise is fulfilled.

How often does this bug happen?

None

Screenshots or Videos

Image

Platform

OS: mac os

Browser: chrome

next.js : 14.2 react : 18

Tanstack Query adapter

None

TanStack Query version

5.8.1

TypeScript version

5.8.3

Additional context

No response

BoubleJ avatar Nov 04 '25 07:11 BoubleJ

please show a minimal runnable reproduction; at best you just swap out whatever you do in your queryFn with Promise.resolve(someStaticJson) to rule out any network issues.

TkDodo avatar Nov 05 '25 12:11 TkDodo