snapshot of https://github.com/TanStack/query.git (main, 2026-09-16; blobs > 131072B omitted)
This commit is contained in:
27
examples/react/load-more-infinite-scroll/.gitignore
vendored
Normal file
27
examples/react/load-more-infinite-scroll/.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/load-more-infinite-scroll/README.md
Normal file
6
examples/react/load-more-infinite-scroll/README.md
Normal file
@@ -0,0 +1,6 @@
|
||||
# Example
|
||||
|
||||
To run this example:
|
||||
|
||||
- `npm install`
|
||||
- `npm run dev`
|
||||
13
examples/react/load-more-infinite-scroll/next.config.js
Normal file
13
examples/react/load-more-infinite-scroll/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
|
||||
23
examples/react/load-more-infinite-scroll/package.json
Normal file
23
examples/react/load-more-infinite-scroll/package.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"name": "@tanstack/query-example-react-load-more-infinite-scroll",
|
||||
"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",
|
||||
"react-intersection-observer": "^9.16.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^19.2.7",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"typescript": "5.8.3"
|
||||
}
|
||||
}
|
||||
13
examples/react/load-more-infinite-scroll/src/pages/about.tsx
Normal file
13
examples/react/load-more-infinite-scroll/src/pages/about.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
export default () => {
|
||||
return (
|
||||
<a
|
||||
href=""
|
||||
onClick={(e) => {
|
||||
window.history.back()
|
||||
e.preventDefault()
|
||||
}}
|
||||
>
|
||||
Back
|
||||
</a>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next'
|
||||
|
||||
export default (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const cursor = parseInt(req.query.cursor) || 0
|
||||
const pageSize = 5
|
||||
|
||||
const data = Array(pageSize)
|
||||
.fill(0)
|
||||
.map((_, i) => {
|
||||
return {
|
||||
name: 'Project ' + (i + cursor) + ` (server time: ${Date.now()})`,
|
||||
id: i + cursor,
|
||||
}
|
||||
})
|
||||
|
||||
const nextId = cursor < 10 ? data[data.length - 1].id + 1 : null
|
||||
const previousId = cursor > -10 ? data[0].id - pageSize : null
|
||||
|
||||
setTimeout(() => res.json({ data, nextId, previousId }), 1000)
|
||||
}
|
||||
121
examples/react/load-more-infinite-scroll/src/pages/index.tsx
Normal file
121
examples/react/load-more-infinite-scroll/src/pages/index.tsx
Normal file
@@ -0,0 +1,121 @@
|
||||
import React from 'react'
|
||||
import Link from 'next/link'
|
||||
import { useInView } from 'react-intersection-observer'
|
||||
import {
|
||||
useInfiniteQuery,
|
||||
QueryClient,
|
||||
QueryClientProvider,
|
||||
} 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>
|
||||
)
|
||||
}
|
||||
|
||||
function Example() {
|
||||
const { ref, inView } = useInView()
|
||||
|
||||
const {
|
||||
status,
|
||||
data,
|
||||
error,
|
||||
isFetching,
|
||||
isFetchingNextPage,
|
||||
isFetchingPreviousPage,
|
||||
fetchNextPage,
|
||||
fetchPreviousPage,
|
||||
hasNextPage,
|
||||
hasPreviousPage,
|
||||
} = useInfiniteQuery({
|
||||
queryKey: ['projects'],
|
||||
queryFn: async ({
|
||||
pageParam,
|
||||
}): Promise<{
|
||||
data: Array<{ name: string; id: number }>
|
||||
previousId: number
|
||||
nextId: number
|
||||
}> => {
|
||||
const response = await fetch(`/api/projects?cursor=${pageParam}`)
|
||||
return await response.json()
|
||||
},
|
||||
initialPageParam: 0,
|
||||
getPreviousPageParam: (firstPage) => firstPage.previousId,
|
||||
getNextPageParam: (lastPage) => lastPage.nextId,
|
||||
})
|
||||
|
||||
React.useEffect(() => {
|
||||
if (inView && hasNextPage && !isFetchingNextPage) {
|
||||
fetchNextPage()
|
||||
}
|
||||
}, [inView, hasNextPage, isFetchingNextPage, fetchNextPage])
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>Infinite Loading</h1>
|
||||
{status === 'pending' ? (
|
||||
<p>Loading...</p>
|
||||
) : status === 'error' ? (
|
||||
<span>Error: {error.message}</span>
|
||||
) : (
|
||||
<>
|
||||
<div>
|
||||
<button
|
||||
onClick={() => fetchPreviousPage()}
|
||||
disabled={!hasPreviousPage || isFetchingPreviousPage}
|
||||
>
|
||||
{isFetchingPreviousPage
|
||||
? 'Loading more...'
|
||||
: hasPreviousPage
|
||||
? 'Load Older'
|
||||
: 'Nothing more to load'}
|
||||
</button>
|
||||
</div>
|
||||
{data.pages.map((page) => (
|
||||
<React.Fragment key={page.nextId}>
|
||||
{page.data.map((project) => (
|
||||
<p
|
||||
style={{
|
||||
border: '1px solid gray',
|
||||
borderRadius: '5px',
|
||||
padding: '10rem 1rem',
|
||||
background: `hsla(${project.id * 30}, 60%, 80%, 1)`,
|
||||
}}
|
||||
key={project.id}
|
||||
>
|
||||
{project.name}
|
||||
</p>
|
||||
))}
|
||||
</React.Fragment>
|
||||
))}
|
||||
<div>
|
||||
<button
|
||||
ref={ref}
|
||||
onClick={() => fetchNextPage()}
|
||||
disabled={!hasNextPage || isFetchingNextPage}
|
||||
>
|
||||
{isFetchingNextPage
|
||||
? 'Loading more...'
|
||||
: hasNextPage
|
||||
? 'Load Newer'
|
||||
: 'Nothing more to load'}
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
{isFetching && !isFetchingNextPage
|
||||
? 'Background Updating...'
|
||||
: null}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<hr />
|
||||
<Link href="/about">Go to another page</Link>
|
||||
<ReactQueryDevtools initialIsOpen />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
20
examples/react/load-more-infinite-scroll/tsconfig.json
Normal file
20
examples/react/load-more-infinite-scroll/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