콘텐츠로 이동

React Example: Optimistic Updates Cache

Source URL: https://tanstack.com/query/latest/docs/framework/react/examples/optimistic-updates-cache

TanStack

Query v5v5

AlphaTry TanStack CLI

Search…

K

Auto

Log In

StartRC

StartRC

Router

Router

Query

Query

Table

Table

DBbeta

DBbeta

AIalpha

AIalpha

Formnew

Formnew

Virtual

Virtual

Pacerbeta

Pacerbeta

Hotkeysalpha

Hotkeysalpha

Storealpha

Storealpha

Devtoolsalpha

Devtoolsalpha

CLIalpha

CLIalpha

More Libraries

More Libraries

BuilderAlpha

BuilderAlpha

FeedBeta

FeedBeta

Maintainers

Maintainers

Partners

Partners

Showcase

Showcase

Blog

Blog

LearnNEW

LearnNEW

Support

Support

Stats

Stats

Discord

Discord

Merch

Merch

GitHub

GitHub

Ethos

Ethos

Tenets

Tenets

Brand Guide

Brand Guide

Docs

Partners

CodeRabbitCodeRabbitCloudflareCloudflareAG GridAG GridSerpAPISerpAPINetlifyNetlifyNeonNeonWorkOSWorkOSClerkClerkConvexConvexElectricElectricPowerSyncPowerSyncSentrySentryRailwayRailwayPrismaPrismaStrapiStrapiUnkeyUnkeyCodeRabbitCodeRabbitCloudflareCloudflareAG GridAG GridSerpAPISerpAPINetlifyNetlifyNeonNeonWorkOSWorkOSClerkClerkConvexConvexElectricElectricPowerSyncPowerSyncSentrySentryRailwayRailwayPrismaPrismaStrapiStrapiUnkeyUnkey

ReactReact

Latest

Search…

K

latest

ReactReact

Latest

Github StackBlitz CodeSandbox

Code ExplorerCode

Interactive SandboxSandbox

  • src

    • pages

      • api

      • index.tsx file iconindex.tsx

  • .gitignore file icon.gitignore

  • README.md file iconREADME.md

  • next.config.js file iconnext.config.js

  • package.json file iconpackage.json

  • tsconfig.json file icontsconfig.json

tsx

import * as React from 'react'
import {
QueryClient,
QueryClientProvider,
queryOptions,
useMutation,
useQuery,
useQueryClient,
} from '@tanstack/react-query'
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
const client = new QueryClient()
type Todos = {
items: ReadonlyArray<{
id: string
text: string
}>
ts: number
}
async function fetchTodos({ signal }: { signal: AbortSignal }): Promise<Todos> {
const response = await fetch('/api/data', { signal })
return await response.json()
}
const todoListOptions = queryOptions({
queryKey: ['todos'],
queryFn: fetchTodos,
})
function Example() {
const queryClient = useQueryClient()
const [text, setText] = React.useState('')
const { isFetching, ...queryInfo } = useQuery(todoListOptions)
const addTodoMutation = useMutation({
mutationFn: async (newTodo: string) => {
const response = await fetch('/api/data', {
method: 'POST',
body: JSON.stringify({ text: newTodo }),
headers: { 'Content-Type': 'application/json' },
})
return await response.json()
},
// When mutate is called:
onMutate: async (newTodo, context) => {
setText('')
// Cancel any outgoing refetch
// (so they don't overwrite our optimistic update)
await context.client.cancelQueries(todoListOptions)
// Snapshot the previous value
const previousTodos = context.client.getQueryData(
todoListOptions.queryKey,
)
// Optimistically update to the new value
if (previousTodos) {
context.client.setQueryData(todoListOptions.queryKey, {
...previousTodos,
items: [
...previousTodos.items,
{ id: Math.random().toString(), text: newTodo },
],
})
}
return { previousTodos }
},
// If the mutation fails,
// use the result returned from onMutate to roll back
onError: (err, variables, onMutateResult, context) => {
if (onMutateResult?.previousTodos) {
context.client.setQueryData<Todos>(
['todos'],
onMutateResult.previousTodos,
)
}
},
// Always refetch after error or success:
onSettled: (data, error, variables, onMutateResult, context) =>
context.client.invalidateQueries({ queryKey: ['todos'] }),
})
return (
<p>
In this example, new items can be created using a mutation. The new item
will be optimistically added to the list in hopes that the server
accepts the item. If it does, the list is refetched with the true items
from the list. Every now and then, the mutation may fail though. When
that happens, the previous list of items is restored and the list is
again refetched from the server.
</p>
<form
onSubmit={(e) => {
e.preventDefault()
addTodoMutation.mutate(text)
}}
>
<input
type="text"
onChange={(event) => setText(event.target.value)}
value={text}
/>
<button disabled={addTodoMutation.isPending}>Create</button>
</form>
{queryInfo.isSuccess && (
<>
{/* The type of queryInfo.data will be narrowed because we check for isSuccess first */}
Updated At: {new Date(queryInfo.data.ts).toLocaleTimeString()}
<ul>
{queryInfo.data.items.map((todo) => (
<li key={todo.id}>{todo.text}</li>
))}
</ul>
{isFetching && <div>Updating in background...</div>}
</>
)}
{queryInfo.isLoading && 'Loading'}
{queryInfo.error instanceof Error && queryInfo.error.message}
)
}
export default function App() {
return (
)
}

CodeRabbitCodeRabbitCloudflareCloudflareAG GridAG GridSerpAPISerpAPINetlifyNetlifyNeonNeonWorkOSWorkOSClerkClerkConvexConvexElectricElectricPowerSyncPowerSyncSentrySentryRailwayRailwayPrismaPrismaStrapiStrapiUnkeyUnkey

“If you’re serious about really understanding React Query, there’s no better way than with query.gg”—Tanner Linsley

Learn More](https://query.gg?s=tanstack)