snapshot of https://github.com/TanStack/query.git (main, 2026-09-16; blobs > 131072B omitted)
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
export interface Todo {
|
||||
id: string
|
||||
text: string
|
||||
createdAt: number
|
||||
}
|
||||
|
||||
export const todos: Array<Todo> = [
|
||||
{
|
||||
id: crypto.randomUUID(),
|
||||
text: 'Buy groceries',
|
||||
createdAt: Date.now() - 3000,
|
||||
},
|
||||
{
|
||||
id: crypto.randomUUID(),
|
||||
text: 'Walk the dog',
|
||||
createdAt: Date.now() - 2000,
|
||||
},
|
||||
{
|
||||
id: crypto.randomUUID(),
|
||||
text: 'Read a book',
|
||||
createdAt: Date.now() - 1000,
|
||||
},
|
||||
]
|
||||
|
||||
export async function getTodos(): Promise<Array<Todo>> {
|
||||
await new Promise((resolve) => setTimeout(resolve, 200))
|
||||
return todos
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import { NextResponse } from 'next/server'
|
||||
import { todos, getTodos, type Todo } from './data'
|
||||
|
||||
export async function GET() {
|
||||
return NextResponse.json(await getTodos())
|
||||
}
|
||||
|
||||
export async function POST(request: Request) {
|
||||
let body: unknown
|
||||
try {
|
||||
body = await request.json()
|
||||
} catch {
|
||||
return NextResponse.json({ error: 'text is required' }, { status: 400 })
|
||||
}
|
||||
|
||||
const text =
|
||||
body !== null &&
|
||||
typeof body === 'object' &&
|
||||
'text' in body &&
|
||||
typeof (body as { text: unknown }).text === 'string'
|
||||
? (body as { text: string }).text.trim()
|
||||
: ''
|
||||
|
||||
if (!text) {
|
||||
return NextResponse.json({ error: 'text is required' }, { status: 400 })
|
||||
}
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 500))
|
||||
|
||||
if (Math.random() < 0.3) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Server error — please try again' },
|
||||
{ status: 500 },
|
||||
)
|
||||
}
|
||||
|
||||
const newTodo: Todo = {
|
||||
id: crypto.randomUUID(),
|
||||
text,
|
||||
createdAt: Date.now(),
|
||||
}
|
||||
|
||||
todos.push(newTodo)
|
||||
|
||||
return NextResponse.json(newTodo, { status: 201 })
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import {
|
||||
QueryClient,
|
||||
defaultShouldDehydrateQuery,
|
||||
environmentManager,
|
||||
} from '@tanstack/react-query'
|
||||
|
||||
function makeQueryClient() {
|
||||
return new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
staleTime: 60 * 1000,
|
||||
},
|
||||
dehydrate: {
|
||||
shouldDehydrateQuery: (query) =>
|
||||
defaultShouldDehydrateQuery(query) ||
|
||||
query.state.status === 'pending',
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
let browserQueryClient: QueryClient | undefined = undefined
|
||||
|
||||
export function getQueryClient() {
|
||||
if (environmentManager.isServer()) {
|
||||
return makeQueryClient()
|
||||
} else {
|
||||
if (!browserQueryClient) browserQueryClient = makeQueryClient()
|
||||
return browserQueryClient
|
||||
}
|
||||
}
|
||||
22
examples/react/nextjs-app-optimistic-updates/app/layout.tsx
Normal file
22
examples/react/nextjs-app-optimistic-updates/app/layout.tsx
Normal file
@@ -0,0 +1,22 @@
|
||||
import Providers from './providers'
|
||||
import type React from 'react'
|
||||
import type { Metadata } from 'next'
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'TanStack Query — Optimistic Updates',
|
||||
description: 'Next.js App Router example with optimistic updates',
|
||||
}
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body>
|
||||
<Providers>{children}</Providers>
|
||||
</body>
|
||||
</html>
|
||||
)
|
||||
}
|
||||
29
examples/react/nextjs-app-optimistic-updates/app/page.tsx
Normal file
29
examples/react/nextjs-app-optimistic-updates/app/page.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
import React from 'react'
|
||||
import { HydrationBoundary, dehydrate, noop } from '@tanstack/react-query'
|
||||
import { getQueryClient } from '@/app/get-query-client'
|
||||
import { getTodos } from '@/app/api/todos/data'
|
||||
import ApproachTabs from '@/components/ApproachTabs'
|
||||
|
||||
export default function Home() {
|
||||
const queryClient = getQueryClient()
|
||||
|
||||
void queryClient
|
||||
.query({
|
||||
queryKey: ['todos'],
|
||||
queryFn: getTodos,
|
||||
})
|
||||
.catch(noop)
|
||||
|
||||
return (
|
||||
<main style={{ maxWidth: 600, margin: '0 auto', padding: '2rem 1rem' }}>
|
||||
<h1>Optimistic Updates with TanStack Query</h1>
|
||||
<p style={{ color: '#666', marginBottom: '1.5rem' }}>
|
||||
Add todos to see optimistic updates in action. The server randomly fails
|
||||
~30% of the time so you can observe automatic rollback.
|
||||
</p>
|
||||
<HydrationBoundary state={dehydrate(queryClient)}>
|
||||
<ApproachTabs />
|
||||
</HydrationBoundary>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
'use client'
|
||||
import { QueryClientProvider } from '@tanstack/react-query'
|
||||
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
|
||||
import { getQueryClient } from '@/app/get-query-client'
|
||||
import type * as React from 'react'
|
||||
|
||||
export default function Providers({ children }: { children: React.ReactNode }) {
|
||||
const queryClient = getQueryClient()
|
||||
|
||||
return (
|
||||
<QueryClientProvider client={queryClient}>
|
||||
{children}
|
||||
<ReactQueryDevtools />
|
||||
</QueryClientProvider>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user