Files
dotfiles/dot_agents/skills/tanstack-query-best-practices/rules/inf-page-params.md
T
Prad Nukala 6406208b35 shell: fish functions -> ~/.local/bin executables + bash/zsh ports; manage ~/.agents
- 16 cross-shell utilities converted to sh/bash chezmoi executables:
  ai c2p cp-appid extract_agents fab gdc gh-sonr-org-migrate ghg-clone
  ghn gho git-summary launch_omp_livediff_overlay lc-elir mdcp mdp pj
  (ghg-clone not named ghg: unrelated cargo binary owns that name)
- cwd/history helpers stay per-shell: yy c cd clip-last + ghg/ghn/gho
  cd-wrappers in bash/functions/ and zsh/functions/, loaded by rc files
- fish keeps only shell-state functions; converted ones now hit PATH
- cp-appid ignore gating moved to .local/bin path
- dot_agents/skills: 82 skills materialized from dotstate storage so
  dot_claude skill symlinks resolve on fresh bootstrap; dotstate
  profile/symlinks.json released .agents/skills .bashrc .zshrc
2026-08-10 15:15:06 -04:00

3.5 KiB

inf-page-params: Always Provide getNextPageParam for Infinite Queries

Priority: MEDIUM

Explanation

useInfiniteQuery requires getNextPageParam to determine how to fetch subsequent pages. This function receives the last page's data and must return the next page parameter, or undefined when there are no more pages.

Bad Example

// Missing getNextPageParam - can't load more pages
const { data, fetchNextPage } = useInfiniteQuery({
  queryKey: ['posts'],
  queryFn: ({ pageParam }) => fetchPosts(pageParam),
  initialPageParam: 1,
  // Missing getNextPageParam - fetchNextPage won't work correctly
})

Good Example: Offset-Based Pagination

const {
  data,
  fetchNextPage,
  hasNextPage,
  isFetchingNextPage,
} = useInfiniteQuery({
  queryKey: ['posts'],
  queryFn: ({ pageParam }) => fetchPosts({ page: pageParam, limit: 20 }),
  initialPageParam: 1,
  getNextPageParam: (lastPage, allPages) => {
    // Return next page number, or undefined if no more pages
    if (lastPage.length < 20) {
      return undefined  // No more pages
    }
    return allPages.length + 1
  },
})

Good Example: Cursor-Based Pagination

interface PostsResponse {
  posts: Post[]
  nextCursor: string | null
}

const { data, fetchNextPage, hasNextPage } = useInfiniteQuery({
  queryKey: ['posts'],
  queryFn: ({ pageParam }): Promise<PostsResponse> =>
    fetchPosts({ cursor: pageParam }),
  initialPageParam: undefined as string | undefined,
  getNextPageParam: (lastPage) => lastPage.nextCursor ?? undefined,
})

Good Example: Bi-directional Pagination

const { data, fetchNextPage, fetchPreviousPage, hasNextPage, hasPreviousPage } =
  useInfiniteQuery({
    queryKey: ['messages', chatId],
    queryFn: ({ pageParam }) => fetchMessages({ chatId, cursor: pageParam }),
    initialPageParam: { direction: 'initial' } as PageParam,
    getNextPageParam: (lastPage) =>
      lastPage.hasMore ? { cursor: lastPage.nextCursor, direction: 'next' } : undefined,
    getPreviousPageParam: (firstPage) =>
      firstPage.hasPrevious
        ? { cursor: firstPage.prevCursor, direction: 'prev' }
        : undefined,
  })

Good Example: With Total Count

interface PaginatedResponse<T> {
  items: T[]
  total: number
  page: number
  pageSize: number
}

const { data, hasNextPage } = useInfiniteQuery({
  queryKey: ['products', filters],
  queryFn: ({ pageParam }) =>
    fetchProducts({ ...filters, page: pageParam, pageSize: 20 }),
  initialPageParam: 1,
  getNextPageParam: (lastPage) => {
    const totalPages = Math.ceil(lastPage.total / lastPage.pageSize)
    if (lastPage.page < totalPages) {
      return lastPage.page + 1
    }
    return undefined
  },
})

Accessing Flattened Data

// data.pages is an array of page responses
// Flatten for easier iteration
const allPosts = data?.pages.flatMap(page => page.posts) ?? []

return (
  <div>
    {allPosts.map(post => (
      <PostCard key={post.id} post={post} />
    ))}
    {hasNextPage && (
      <button
        onClick={() => fetchNextPage()}
        disabled={isFetchingNextPage}
      >
        {isFetchingNextPage ? 'Loading...' : 'Load More'}
      </button>
    )}
  </div>
)

Context

  • getNextPageParam returning undefined sets hasNextPage to false
  • For bi-directional scrolling, also provide getPreviousPageParam
  • initialPageParam is required and sets the first page parameter
  • Use maxPages option to limit stored pages for memory management
  • Consider select to transform page structure for component consumption