Created
September 10, 2025 07:37
-
-
Save trinhvanminh/5f07d0ef715e87b658b8106eea218801 to your computer and use it in GitHub Desktop.
@TanStack optimistic-updates https://tanstack.com/query/latest/docs/framework/react/guides/optimistic-updates
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import React from 'react'; | |
| import ReactDOM from 'react-dom/client'; | |
| import { | |
| QueryClient, | |
| QueryClientProvider, | |
| useMutation, | |
| useQuery, | |
| } from '@tanstack/react-query'; | |
| import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; | |
| const queryClient = new QueryClient(); | |
| export default function App() { | |
| return ( | |
| <QueryClientProvider client={queryClient}> | |
| <ReactQueryDevtools /> | |
| <Example /> | |
| </QueryClientProvider> | |
| ); | |
| } | |
| const wait = async (ms: number = 300) => { | |
| return await new Promise((resolve) => { | |
| setTimeout(() => { | |
| resolve(); | |
| }, ms); | |
| }); | |
| }; | |
| function Example() { | |
| const { isPending, error, data, isFetching, isFetched } = useQuery({ | |
| queryKey: ['todos'], | |
| queryFn: async () => { | |
| const res = await fetch( | |
| 'https://68c106ca0b196b9ce1c5a7b3.mockapi.io/api/todos' | |
| ); | |
| if (!res.ok) throw new Error('Network response was not ok'); | |
| return res.json(); | |
| }, | |
| }); | |
| const addTodoMutation = useMutation({ | |
| mutationFn: async (newTodo: string) => { | |
| if (newTodo.startsWith('error')) { | |
| throw new Error('Failed to add todo'); | |
| } | |
| const res = await fetch( | |
| 'https://68c106ca0b196b9ce1c5a7b3.mockapi.io/api/todos', | |
| { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ | |
| text: newTodo, | |
| }), | |
| } | |
| ); | |
| if (!res.ok) throw new Error('Failed to add todo'); | |
| return res.json(); | |
| }, | |
| onSettled: () => queryClient.invalidateQueries({ queryKey: ['todos'] }), | |
| }); | |
| if (isPending) return 'Loading...'; | |
| if (error) return 'An error has occurred: ' + error.message; | |
| return ( | |
| <div> | |
| <button | |
| onClick={() => | |
| addTodoMutation.mutate(`test ${new Date().toISOString()}`) | |
| } | |
| > | |
| Add todo | |
| </button> | |
| <button | |
| onClick={() => | |
| addTodoMutation.mutate(`error ${new Date().toISOString()}`) | |
| } | |
| > | |
| Add error todo | |
| </button> | |
| <ul> | |
| {data.map((todo) => ( | |
| <li key={todo.id}>{todo.text.slice(0, 40)}</li> | |
| ))} | |
| {addTodoMutation.isPending && ( | |
| <li style={{ opacity: 0.5 }}>{addTodoMutation.variables}</li> | |
| )} | |
| {addTodoMutation.isError && ( | |
| <li style={{ color: 'red' }}> | |
| {addTodoMutation.variables} | |
| <button | |
| onClick={() => | |
| addTodoMutation.mutate( | |
| addTodoMutation.variables.replace('error', 'no-error') | |
| ) | |
| } | |
| > | |
| Retry | |
| </button> | |
| </li> | |
| )} | |
| </ul> | |
| <div>{isFetching ? 'Updating...' : ''}</div> | |
| </div> | |
| ); | |
| } | |
| const rootElement = document.getElementById('root') as HTMLElement; | |
| ReactDOM.createRoot(rootElement).render(<App />); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment