snapshot of https://github.com/TanStack/query.git (main, 2026-09-16; blobs > 131072B omitted)
Some checks failed
autofix.ci / autofix (push) Failing after 4s
Release / Release (push) Has been skipped
GitHub Actions Security Analysis / Run zizmor (push) Failing after 2s

This commit is contained in:
upstream-mirror
2026-09-16 01:13:08 +05:00
commit 3a013ccdfe
3570 changed files with 490194 additions and 0 deletions

View File

@@ -0,0 +1,46 @@
'use client'
import React, { useState } from 'react'
import TodoListUI from './TodoListUI'
import TodoListCache from './TodoListCache'
type Tab = 'ui-variables' | 'cache'
export default function ApproachTabs() {
const [activeTab, setActiveTab] = useState<Tab>('ui-variables')
const tabStyle = (tab: Tab): React.CSSProperties => ({
padding: '0.5rem 1rem',
border: 'none',
borderBottom:
activeTab === tab ? '2px solid #0070f3' : '2px solid transparent',
background: 'none',
cursor: 'pointer',
fontWeight: activeTab === tab ? 600 : 400,
color: activeTab === tab ? '#0070f3' : '#555',
})
return (
<div>
<div
style={{
display: 'flex',
borderBottom: '1px solid #ddd',
marginBottom: '1.5rem',
}}
>
<button
style={tabStyle('ui-variables')}
onClick={() => setActiveTab('ui-variables')}
>
Via UI Variables
</button>
<button style={tabStyle('cache')} onClick={() => setActiveTab('cache')}>
Via Cache Manipulation
</button>
</div>
{activeTab === 'ui-variables' ? <TodoListUI /> : <TodoListCache />}
</div>
)
}

View File

@@ -0,0 +1,130 @@
'use client'
import React, { useState } from 'react'
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import type { Todo } from '@/app/api/todos/data'
interface MutationContext {
previousTodos: Array<Todo> | undefined
optimisticId: string
}
async function fetchTodos(): Promise<Array<Todo>> {
const res = await fetch('/api/todos')
if (!res.ok) throw new Error('Failed to fetch todos')
return res.json()
}
async function addTodo(text: string): Promise<Todo> {
const res = await fetch('/api/todos', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ text }),
})
if (!res.ok) {
const err = (await res.json()) as { error: string }
throw new Error(err.error)
}
return res.json()
}
export default function TodoListCache() {
const queryClient = useQueryClient()
const [inputValue, setInputValue] = useState('')
const [lastError, setLastError] = useState<string | null>(null)
const { data: todos = [] } = useQuery({
queryKey: ['todos'],
queryFn: fetchTodos,
})
const addTodoMutation = useMutation<Todo, Error, string, MutationContext>({
mutationFn: addTodo,
onMutate: async (text) => {
setLastError(null)
await queryClient.cancelQueries({ queryKey: ['todos'] })
const previousTodos = queryClient.getQueryData<Array<Todo>>(['todos'])
const optimisticId = `optimistic-${Date.now()}`
const optimisticTodo: Todo = {
id: optimisticId,
text,
createdAt: Date.now(),
}
queryClient.setQueryData<Array<Todo>>(['todos'], (old = []) => [
...old,
optimisticTodo,
])
return { previousTodos, optimisticId }
},
onError: (_err, _text, context) => {
setLastError(_err.message)
if (context?.previousTodos !== undefined) {
queryClient.setQueryData(['todos'], context.previousTodos)
} else if (context?.optimisticId) {
queryClient.setQueryData<Array<Todo>>(['todos'], (old = []) =>
old.filter((todo) => todo.id !== context.optimisticId),
)
}
},
onSettled: () => {
void queryClient.invalidateQueries({ queryKey: ['todos'] })
},
})
function handleSubmit(e: React.FormEvent) {
e.preventDefault()
const text = inputValue.trim()
if (!text) return
addTodoMutation.mutate(text)
setInputValue('')
}
return (
<div>
<p style={{ fontSize: '0.875rem', color: '#555', marginBottom: '1rem' }}>
<strong>Approach 2 — via cache manipulation:</strong>{' '}
<code>onMutate</code> snapshots the cache and writes the optimistic item
in. <code>onError</code> restores the snapshot on failure.
</p>
<form
onSubmit={handleSubmit}
style={{ display: 'flex', gap: 8, marginBottom: '1rem' }}
>
<input
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
placeholder="New todo…"
style={{ flex: 1, padding: '0.4rem 0.6rem', fontSize: '1rem' }}
/>
<button type="submit" disabled={addTodoMutation.isPending}>
Add
</button>
</form>
{lastError && (
<p style={{ color: 'red', marginBottom: '0.5rem' }}>{lastError}</p>
)}
<ul style={{ listStyle: 'none', padding: 0 }}>
{todos.map((todo) => (
<li
key={todo.id}
style={{
padding: '0.4rem 0',
borderBottom: '1px solid #eee',
opacity: todo.id.startsWith('optimistic-') ? 0.5 : 1,
}}
>
{todo.text}
{todo.id.startsWith('optimistic-') && <em> (saving…)</em>}
</li>
))}
</ul>
</div>
)
}

View File

@@ -0,0 +1,102 @@
'use client'
import React, { useState } from 'react'
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import type { Todo } from '@/app/api/todos/data'
async function fetchTodos(): Promise<Array<Todo>> {
const res = await fetch('/api/todos')
if (!res.ok) throw new Error('Failed to fetch todos')
return res.json()
}
async function addTodo(text: string): Promise<Todo> {
const res = await fetch('/api/todos', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ text }),
})
if (!res.ok) {
const err = (await res.json()) as { error: string }
throw new Error(err.error)
}
return res.json()
}
export default function TodoListUI() {
const queryClient = useQueryClient()
const [inputValue, setInputValue] = useState('')
const { data: todos = [] } = useQuery({
queryKey: ['todos'],
queryFn: fetchTodos,
})
const addTodoMutation = useMutation({
mutationFn: addTodo,
onSettled: () => {
void queryClient.invalidateQueries({ queryKey: ['todos'] })
},
})
function handleSubmit(e: React.FormEvent) {
e.preventDefault()
const text = inputValue.trim()
if (!text) return
addTodoMutation.mutate(text)
setInputValue('')
}
return (
<div>
<p style={{ fontSize: '0.875rem', color: '#555', marginBottom: '1rem' }}>
<strong>Approach 1 — via UI variables:</strong> The pending item is
rendered directly from <code>mutation.variables</code>. No cache
manipulation needed. On error the pending item simply disappears.
</p>
<form
onSubmit={handleSubmit}
style={{ display: 'flex', gap: 8, marginBottom: '1rem' }}
>
<input
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
placeholder="New todo…"
style={{ flex: 1, padding: '0.4rem 0.6rem', fontSize: '1rem' }}
/>
<button type="submit" disabled={addTodoMutation.isPending}>
Add
</button>
</form>
{addTodoMutation.isError && (
<p style={{ color: 'red', marginBottom: '0.5rem' }}>
{addTodoMutation.error.message}
</p>
)}
<ul style={{ listStyle: 'none', padding: 0 }}>
{todos.map((todo) => (
<li
key={todo.id}
style={{ padding: '0.4rem 0', borderBottom: '1px solid #eee' }}
>
{todo.text}
</li>
))}
{addTodoMutation.isPending && (
<li
style={{
padding: '0.4rem 0',
borderBottom: '1px solid #eee',
opacity: 0.5,
}}
>
{addTodoMutation.variables} <em>(saving…)</em>
</li>
)}
</ul>
</div>
)
}