snapshot of https://github.com/TanStack/query.git (main, 2026-09-16; blobs > 131072B omitted)
This commit is contained in:
27
examples/react/pagination/.gitignore
vendored
Normal file
27
examples/react/pagination/.gitignore
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
pnpm-lock.yaml
|
||||
yarn.lock
|
||||
package-lock.json
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
6
examples/react/pagination/README.md
Normal file
6
examples/react/pagination/README.md
Normal file
@@ -0,0 +1,6 @@
|
||||
# Example
|
||||
|
||||
To run this example:
|
||||
|
||||
- `npm install`
|
||||
- `npm run dev`
|
||||
13
examples/react/pagination/next.config.js
Normal file
13
examples/react/pagination/next.config.js
Normal file
@@ -0,0 +1,13 @@
|
||||
// @ts-check
|
||||
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
eslint: {
|
||||
ignoreDuringBuilds: true,
|
||||
},
|
||||
typescript: {
|
||||
ignoreBuildErrors: true,
|
||||
},
|
||||
}
|
||||
|
||||
export default nextConfig
|
||||
22
examples/react/pagination/package.json
Normal file
22
examples/react/pagination/package.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "@tanstack/query-example-react-pagination",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "next dev --webpack",
|
||||
"build": "next build",
|
||||
"start": "next start"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tanstack/react-query": "^5.102.8",
|
||||
"@tanstack/react-query-devtools": "^5.102.8",
|
||||
"next": "^16.0.7",
|
||||
"react": "^19.2.1",
|
||||
"react-dom": "^19.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^19.2.7",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"typescript": "5.8.3"
|
||||
}
|
||||
}
|
||||
21
examples/react/pagination/src/pages/api/projects.ts
Normal file
21
examples/react/pagination/src/pages/api/projects.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next'
|
||||
|
||||
export default async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const page = parseInt(req.query.page) || 0
|
||||
|
||||
const pageSize = 10
|
||||
|
||||
const projects = Array(pageSize)
|
||||
.fill(0)
|
||||
.map((_, i) => {
|
||||
const id = page * pageSize + (i + 1)
|
||||
return {
|
||||
name: 'Project ' + id,
|
||||
id,
|
||||
}
|
||||
})
|
||||
|
||||
await new Promise((r) => setTimeout(r, 1000))
|
||||
|
||||
res.json({ projects, hasMore: page < 9 })
|
||||
}
|
||||
102
examples/react/pagination/src/pages/index.tsx
Normal file
102
examples/react/pagination/src/pages/index.tsx
Normal file
@@ -0,0 +1,102 @@
|
||||
import React from 'react'
|
||||
import {
|
||||
QueryClient,
|
||||
QueryClientProvider,
|
||||
keepPreviousData,
|
||||
noop,
|
||||
useQuery,
|
||||
useQueryClient,
|
||||
} from '@tanstack/react-query'
|
||||
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
|
||||
|
||||
const queryClient = new QueryClient()
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<Example />
|
||||
</QueryClientProvider>
|
||||
)
|
||||
}
|
||||
|
||||
const fetchProjects = async (
|
||||
page = 0,
|
||||
): Promise<{
|
||||
projects: Array<{ name: string; id: number }>
|
||||
hasMore: boolean
|
||||
}> => {
|
||||
const response = await fetch(`/api/projects?page=${page}`)
|
||||
return await response.json()
|
||||
}
|
||||
|
||||
function Example() {
|
||||
const queryClient = useQueryClient()
|
||||
const [page, setPage] = React.useState(0)
|
||||
|
||||
const { status, data, error, isFetching, isPlaceholderData } = useQuery({
|
||||
queryKey: ['projects', page],
|
||||
queryFn: () => fetchProjects(page),
|
||||
placeholderData: keepPreviousData,
|
||||
staleTime: 5000,
|
||||
})
|
||||
|
||||
// Prefetch the next page!
|
||||
React.useEffect(() => {
|
||||
if (!isPlaceholderData && data?.hasMore) {
|
||||
queryClient
|
||||
.query({
|
||||
queryKey: ['projects', page + 1],
|
||||
queryFn: () => fetchProjects(page + 1),
|
||||
})
|
||||
.catch(noop)
|
||||
}
|
||||
}, [data, isPlaceholderData, page, queryClient])
|
||||
|
||||
return (
|
||||
<div>
|
||||
<p>
|
||||
In this example, each page of data remains visible as the next page is
|
||||
fetched. The buttons and capability to proceed to the next page are also
|
||||
suppressed until the next page cursor is known. Each page is cached as a
|
||||
normal query too, so when going to previous pages, you'll see them
|
||||
instantaneously while they are also refetched invisibly in the
|
||||
background.
|
||||
</p>
|
||||
{status === 'pending' ? (
|
||||
<div>Loading...</div>
|
||||
) : status === 'error' ? (
|
||||
<div>Error: {error.message}</div>
|
||||
) : (
|
||||
// `data` will either resolve to the latest page's data
|
||||
// or if fetching a new page, the last successful page's data
|
||||
<div>
|
||||
{data.projects.map((project) => (
|
||||
<p key={project.id}>{project.name}</p>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
<div>Current Page: {page + 1}</div>
|
||||
<button
|
||||
onClick={() => setPage((old) => Math.max(old - 1, 0))}
|
||||
disabled={page === 0}
|
||||
>
|
||||
Previous Page
|
||||
</button>{' '}
|
||||
<button
|
||||
onClick={() => {
|
||||
setPage((old) => (data?.hasMore ? old + 1 : old))
|
||||
}}
|
||||
disabled={isPlaceholderData || !data?.hasMore}
|
||||
>
|
||||
Next Page
|
||||
</button>
|
||||
{
|
||||
// Since the last page's data potentially sticks around between page requests,
|
||||
// we can use `isFetching` to show a background loading
|
||||
// indicator since our `status === 'pending'` state won't be triggered
|
||||
isFetching ? <span> Loading...</span> : null
|
||||
}{' '}
|
||||
<ReactQueryDevtools initialIsOpen />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
20
examples/react/pagination/tsconfig.json
Normal file
20
examples/react/pagination/tsconfig.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Bundler",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "react-jsx",
|
||||
"incremental": true,
|
||||
"target": "ES2017"
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
Reference in New Issue
Block a user