TanStack Query Infinite Queries & Cursor Pagination

Implementieren Sie effizientes Infinite Scrolling und Cursor-basierte Pagination mit TanStack Query – mit useInfiniteQuery, Prefetching und Datensynchronisation.

Sam Rivera
Sam Rivera
1. Juni 202611 min read
Auch verfügbar auf:English · 中文

TL;DR: TanStack Querys useInfiniteQuery bewältigt Cursor- und Offset-basierte Pagination mit integriertem State-Management für Seitenansammlung, Richtung und Prefetching. Dieser Leitfaden behandelt Cursor-basierte Pagination mit TanStack Start Server Functions, Reverse Pagination für Chat-ähnliche UIs, Stale-While-Revalidate für Infinite Lists und Optimistic Prepending für Echtzeit-Updates.


Einleitung

Pagination ist überraschend komplex. Offset-basierte Pagination funktioniert nicht mehr, wenn Elemente hinzugefügt oder entfernt werden. Cursor-basierte Pagination erfordert sorgfältiges State-Management. Infinite Scroll bringt Edge Cases beim Prefetching, Race Conditions und Speicherwachstum mit sich.

TanStack Querys useInfiniteQuery abstrahiert den Großteil dieser Komplexität. Es verwaltet die Seitenansammlung, stellt fetchNextPage- und fetchPreviousPage-Callbacks bereit und integriert sich nahtlos in TanStack Routers Preloading-System.


Cursor-Pagination mit Server Functions

tsx
import { createServerFn } from '@tanstack/react-start'
import { z } from 'zod'
import { db } from '../db'

export const listProductsFn = createServerFn({ method: 'GET' })
  .validator(z.object({
    cursor: z.string().optional(),
    limit: z.number().default(20),
  }))
  .handler(async ({ data }) => {
    const { cursor, limit } = data
    const products = await db
      .selectFrom('products')
      .orderBy('createdAt', 'desc')
      .limit(limit + 1)
      .cursor(cursor ? { after: cursor } : undefined)
      .execute()

    const hasMore = products.length > limit
    const items = hasMore ? products.slice(0, limit) : products
    const nextCursor = hasMore ? items[items.length - 1].id : null

    return { items, nextCursor }
  })

Der useInfiniteQuery-Hook

tsx
import { useInfiniteQuery } from '@tanstack/react-query'
import { listProductsFn } from '../server/products'

function ProductList() {
  const { data, fetchNextPage, hasNextPage, isFetchingNextPage } =
    useInfiniteQuery({
      queryKey: ['products'],
      queryFn: ({ pageParam }) =>
        listProductsFn({ data: { cursor: pageParam } }),
      initialPageParam: undefined,
      getNextPageParam: (lastPage) => lastPage.nextCursor,
    })

  return (
    <div>
      {data?.pages.map((page) =>
        page.items.map((product) => <ProductCard key={product.id} product={product} />)
      )}
      {hasNextPage && (
        <button onClick={() => fetchNextPage()} disabled={isFetchingNextPage}>
          {isFetchingNextPage ? 'Loading...' : 'Load More'}
        </button>
      )}
    </div>
  )
}

Infinite Scroll mit Intersection Observer

tsx
function InfiniteProductList() {
  const observerRef = useRef<HTMLDivElement>(null)

  useEffect(() => {
    const observer = new IntersectionObserver(
      (entries) => {
        if (entries[0].isIntersecting && hasNextPage && !isFetchingNextPage) {
          fetchNextPage()
        }
      },
      { threshold: 0.1 }
    )
    if (observerRef.current) observer.observe(observerRef.current)
    return () => observer.disconnect()
  }, [fetchNextPage, hasNextPage, isFetchingNextPage])

  return (
    <div>
      {items}
      <div ref={observerRef} />
    </div>
  )
}

Vergleich der Pagination-Strategien

StrategieVorteileNachteileOptimal für
Offset (LIMIT/OFFSET)Einfach, direkt zu Seite N springenFunktioniert nicht bei Einfügungen/LöschungenStatische Datensätze
Cursor (Keyset)Stabil, schnell bei großen DatenmengenSeiten nicht überspringbarDynamische Feeds
ZeitbasiertNatürliche SortierungUngleiche SeitengrößenAktivitätsströme
Search-after (Elastic)Funktioniert mit VolltextsucheDB-spezifischSuchergebnisse

Fazit

useInfiniteQuery mit Cursor-basierter Pagination bietet die beste UX für dynamische Datenfeeds. In Kombination mit TanStack Start Server Functions erhalten Sie typsichere Pagination von der Datenbank bis zur UI – mit minimalem Boilerplate.

Für serverseitige Pagination-Muster empfehlen wir TanStack Query: Server Functions Best Practices. Meistern Sie das Cache-Management mit Cache Invalidation Strategies. Wenn Sie große paginierte Listen rendern, lässt sich TanStack Virtual Scrolling natürlich mit Infinite Queries kombinieren, und der Leitfaden SaaS Database Architecture behandelt die zugrunde liegenden Datenstrukturen für effiziente Pagination.