useActorInfiniteQuery
useActorInfiniteQuery is a React hook for fetching paginated data from Internet Computer canisters. It wraps TanStack Query’s useInfiniteQuery with canister-specific functionality.
Import
Section titled “Import”import { createActorHooks } from "@ic-reactor/react"
const { useActorInfiniteQuery } = createActorHooks(backend)function ItemList() { const { data, fetchNextPage, hasNextPage, isFetchingNextPage } = useActorInfiniteQuery({ functionName: "getItems", getArgs: (pageParam) => [{ offset: pageParam, limit: 10 }] as const, initialPageParam: 0, getNextPageParam: (lastPage) => lastPage.nextOffset ?? undefined, })
return ( <div> {data?.pages.map((page, i) => ( <div key={i}> {page.items.map((item) => ( <Item key={item.id} data={item} /> ))} </div> ))}
<button onClick={() => fetchNextPage()} disabled={!hasNextPage || isFetchingNextPage} > {isFetchingNextPage ? "Loading more..." : hasNextPage ? "Load More" : "No more items"} </button> </div> )}Options
Section titled “Options”Required Options
Section titled “Required Options”| Option | Type | Description |
|---|---|---|
functionName | string | The canister method to call |
getArgs | (pageParam) => Args | Function that returns args for each page |
initialPageParam | TPageParam | Initial page parameter |
getNextPageParam | function | Returns next page param or undefined |
Optional Options
Section titled “Optional Options”| Option | Type | Default | Description |
|---|---|---|---|
getPreviousPageParam | function | - | Returns previous page param for bi-directional |
maxPages | number | - | Maximum number of pages to keep in cache |
enabled | boolean | true | Whether the query should run |
staleTime | number | 0 | Time in ms before data is stale |
gcTime | number | 300000 | Time before unused data is GC’d |
refetchOnWindowFocus | boolean | true | Refetch on window focus |
select | function | - | Transform the data |
queryKey | array | - | Additional query key segments |
Callback Signatures
Section titled “Callback Signatures”getArgs: (pageParam: TPageParam) => Args
getNextPageParam: ( lastPage: TData, allPages: TData[], lastPageParam: TPageParam, allPageParams: TPageParam[]) => TPageParam | undefined | null
getPreviousPageParam: ( firstPage: TData, allPages: TData[], firstPageParam: TPageParam, allPageParams: TPageParam[]) => TPageParam | undefined | nullReturn Value
Section titled “Return Value”| Property | Type | Description |
|---|---|---|
data | InfiniteData<TData> | Object with pages and pageParams arrays |
data.pages | TData[] | Array of all fetched pages |
data.pageParams | TPageParam[] | Array of page parameters used |
error | Error | null | Error if query failed |
status | 'pending' | 'error' | 'success' | Query status |
isPending | boolean | True if no data yet |
isFetching | boolean | True if any fetch in progress |
isFetchingNextPage | boolean | True if fetching next page |
isFetchingPreviousPage | boolean | True if fetching previous page |
isRefetching | boolean | True if refetching all pages |
hasNextPage | boolean | True if more pages available |
hasPreviousPage | boolean | True if previous pages available |
fetchNextPage | function | Fetch the next page |
fetchPreviousPage | function | Fetch the previous page |
refetch | function | Refetch all pages |
Examples
Section titled “Examples”Offset-Based Pagination
Section titled “Offset-Based Pagination”const { data, fetchNextPage, hasNextPage } = useActorInfiniteQuery({ functionName: "getItems", getArgs: (offset) => [{ offset, limit: 20 }], initialPageParam: 0, getNextPageParam: (lastPage, allPages) => { // If we got fewer items than limit, no more pages return lastPage.items.length < 20 ? undefined : allPages.length * 20 // Next offset },})Cursor-Based Pagination
Section titled “Cursor-Based Pagination”const { data, fetchNextPage, hasNextPage } = useActorInfiniteQuery({ functionName: "getItems", getArgs: (cursor) => [cursor ? { after: cursor } : {}], initialPageParam: null as string | null, getNextPageParam: (lastPage) => lastPage.nextCursor ?? undefined,})Page Number Pagination
Section titled “Page Number Pagination”const { data, fetchNextPage, hasNextPage } = useActorInfiniteQuery({ functionName: "getItemsByPage", getArgs: (page) => [{ page, perPage: 10 }], initialPageParam: 1, getNextPageParam: (lastPage, allPages, lastPageParam) => { return lastPage.hasMore ? lastPageParam + 1 : undefined },})Bi-Directional Infinite List
Section titled “Bi-Directional Infinite List”const { data, fetchNextPage, fetchPreviousPage, hasNextPage, hasPreviousPage } = useActorInfiniteQuery({ functionName: "getTimeline", getArgs: (timestamp) => [{ around: timestamp, limit: 20 }], initialPageParam: Date.now(), getNextPageParam: (lastPage) => lastPage.oldestTimestamp, getPreviousPageParam: (firstPage) => firstPage.newestTimestamp, })
return ( <div> <button onClick={() => fetchPreviousPage()} disabled={!hasPreviousPage}> Load newer </button>
{data?.pages.map((page, i) => ( <TimelineItems key={i} items={page.items} /> ))}
<button onClick={() => fetchNextPage()} disabled={!hasNextPage}> Load older </button> </div>)Infinite Scroll
Section titled “Infinite Scroll”import { useInView } from "react-intersection-observer"import { useEffect } from "react"
function InfiniteList() { const { ref, inView } = useInView()
const { data, fetchNextPage, hasNextPage, isFetchingNextPage } = useActorInfiniteQuery({ functionName: "getItems", getArgs: (offset) => [{ offset, limit: 20 }], initialPageParam: 0, getNextPageParam: (lastPage, allPages) => lastPage.items.length < 20 ? undefined : allPages.length * 20, })
// Auto-fetch when sentinel comes into view useEffect(() => { if (inView && hasNextPage && !isFetchingNextPage) { fetchNextPage() } }, [inView, hasNextPage, isFetchingNextPage, fetchNextPage])
return ( <div> {data?.pages.map((page, i) => ( <div key={i}> {page.items.map((item) => ( <ItemCard key={item.id} item={item} /> ))} </div> ))}
{/* Sentinel element */} <div ref={ref}>{isFetchingNextPage && <LoadingSpinner />}</div> </div> )}With Select for Flattened Data
Section titled “With Select for Flattened Data”const { data: items } = useActorInfiniteQuery({ functionName: "getItems", getArgs: (offset) => [{ offset, limit: 20 }], initialPageParam: 0, getNextPageParam: (lastPage, allPages) => lastPage.items.length < 20 ? undefined : allPages.length * 20, // Flatten all pages into a single array select: (data) => ({ pages: data.pages.flatMap((page) => page.items), pageParams: data.pageParams, }),})
// items.pages is now a flat array of all itemsLimiting Cached Pages
Section titled “Limiting Cached Pages”const { data } = useActorInfiniteQuery({ functionName: "getItems", getArgs: (offset) => [{ offset, limit: 20 }], initialPageParam: 0, getNextPageParam: (lastPage, allPages) => lastPage.items.length < 20 ? undefined : allPages.length * 20, maxPages: 3, // Only keep last 3 pages in cache})See Also
Section titled “See Also”- useActorSuspenseInfiniteQuery — Suspense version
- useActorQuery — Regular queries
- Queries Guide — In-depth query patterns
- createActorHooks — Creating hooks