👀 Check out the changes in Suspensive v2. read more →
Documentation
@suspensive/react-query
useSuspenseInfiniteQuery

useSuspenseInfiniteQuery

Return type of this hook have no isLoading, isError property. because <Suspense/> and <ErrorBoundary/> will guarantee this hook's data. In addition, this hook's options have default suspense: true. and you can provide new options to this hook like useInfiniteQuery (opens in a new tab) of @tanstack/react-query.

import { useSuspenseInfiniteQuery } from '@suspensive/react-query'
 
const Example = () => {
  const query = useSuspenseInfiniteQuery({
    queryKey,
    queryFn,
  }) // suspense:true is default.
 
  // No need to do type narrowing by isSuccess.
  query.data // InfiniteData<TData>
}
import { useSuspenseInfiniteQuery } from '@suspensive/react-query'
import React from 'react'
import { getPosts } from './api'

export const PostList = () => {
  const { data, isFetchingNextPage, isFetched, hasNextPage, fetchNextPage } =
    useSuspenseInfiniteQuery({
      queryKey: ['posts'],
      queryFn: ({ pageParam = 1 }) => getPosts(pageParam),
      getNextPageParam: (lastPage, allPages) => {
        return lastPage.skip + lastPage.limit < lastPage.total ? allPages.length + 1 : undefined
      },
    })

  React.useEffect(() => {
    if (!isFetchingNextPage && isFetched) {
      window.scrollTo({
        top: document.body.scrollHeight,
        behavior: 'smooth',
      })
    }
  }, [isFetchingNextPage, isFetched])

  return (
    <div style={{ display: 'flex', flexDirection: 'column', marginBottom: '100px' }}>
      <ol>
        {data.pages.map((page, i) => (
          <React.Fragment key={i}>
            {page.data.map((post) => (
              <li key={post.id} style={{ marginBottom: '10px' }}>
                {post.title}
              </li>
            ))}
          </React.Fragment>
        ))}
      </ol>
      <button onClick={() => fetchNextPage()} disabled={!hasNextPage || isFetchingNextPage}>
        {isFetchingNextPage
          ? 'Loading more...'
          : hasNextPage
            ? 'Load More'
            : 'Nothing more to load'}
      </button>
    </div>
  )
}

Motivation

If you turn suspense on in @tanstack/react-query, You can use useInfiniteQuery (opens in a new tab) with <Suspense/> and <ErrorBoundary/>.

import { useInfiniteQuery } from '@tanstack/react-query'
 
const Example = () => {
  const query = useInfiniteQuery({
    queryKey,
    queryFn,
    suspense: true,
  })
 
  query.data // InfiniteData<TData> | undefined
 
  if (query.isSuccess) {
    query.data // InfiniteData<TData>
  }
}

but useInfiniteQuery's return type:query.data will be always fulfilled because of <Suspense/> as parent of this component.

This is why @suspensive/react-query provide useSuspenseInfiniteQuery.

💡

Concentrate on only Success.

Now, we can concentrate component as any fetching will be always success.