From 00b0a56023d5f6aafbba4c5a6232602ac40e5923 Mon Sep 17 00:00:00 2001 From: Lucas Leung Date: Thu, 20 Aug 2026 17:13:22 +0800 Subject: [PATCH 1/5] Add guides in svelte query docs --- docs/config.json | 29 ++ .../guides/invalidations-from-mutations.md | 16 ++ docs/framework/svelte/guides/mutations.md | 263 ++++++++++++++++++ docs/framework/svelte/guides/network-mode.md | 5 + .../svelte/guides/optimistic-updates.md | 154 ++++++++++ docs/framework/svelte/guides/queries.md | 90 ++++++ .../svelte/guides/query-cancellation.md | 160 +++++++++++ 7 files changed, 717 insertions(+) create mode 100644 docs/framework/svelte/guides/invalidations-from-mutations.md create mode 100644 docs/framework/svelte/guides/mutations.md create mode 100644 docs/framework/svelte/guides/network-mode.md create mode 100644 docs/framework/svelte/guides/optimistic-updates.md create mode 100644 docs/framework/svelte/guides/queries.md create mode 100644 docs/framework/svelte/guides/query-cancellation.md diff --git a/docs/config.json b/docs/config.json index 556ed2b67d9..aa16e1f47f8 100644 --- a/docs/config.json +++ b/docs/config.json @@ -526,6 +526,35 @@ } ] }, + { + "label": "svelte", + "children": [ + { + "label": "Queries", + "to": "framework/svelte/guides/queries" + }, + { + "label": "Network Mode", + "to": "framework/svelte/guides/network-mode" + }, + { + "label": "Mutations", + "to": "framework/svelte/guides/mutations" + }, + { + "label": "Invalidations from Mutations", + "to": "framework/svelte/guides/invalidations-from-mutations" + }, + { + "label": "Optimistic Updates", + "to": "framework/svelte/guides/optimistic-updates" + }, + { + "label": "Query Cancellation", + "to": "framework/svelte/guides/query-cancellation" + } + ] + }, { "label": "vue", "children": [ diff --git a/docs/framework/svelte/guides/invalidations-from-mutations.md b/docs/framework/svelte/guides/invalidations-from-mutations.md new file mode 100644 index 00000000000..44e316c1459 --- /dev/null +++ b/docs/framework/svelte/guides/invalidations-from-mutations.md @@ -0,0 +1,16 @@ +--- +id: invalidations-from-mutations +title: Invalidations from Mutations +ref: docs/framework/react/guides/invalidations-from-mutations.md +replace: + { + 'React': 'Svelte', + '@tanstack/react-query': '@tanstack/svelte-query', + 'useMutation[(]': 'createMutation(() => ', + 'useMutation': 'createMutation', + 'useQuery[(]': 'createQuery(() => ', + ', useQueryClient': ', QueryClient', + 'useQueryClient': 'new QueryClient', + 'hook': 'function', + } +--- diff --git a/docs/framework/svelte/guides/mutations.md b/docs/framework/svelte/guides/mutations.md new file mode 100644 index 00000000000..26d053cdf65 --- /dev/null +++ b/docs/framework/svelte/guides/mutations.md @@ -0,0 +1,263 @@ +--- +id: mutations +title: Mutations +ref: docs/framework/react/guides/mutations.md +replace: { 'useMutation': 'createMutation', 'hook': 'function' } +--- + +[//]: # 'Example' + +```svelte + + +
+ {#if mutation.isPending} + Adding todo... + {:else if mutation.isError} +
An error occurred: {mutation.error.message}
+ {:else if mutation.isSuccess} +
Todo added!
+ {/if} + +
+``` + +[//]: # 'Example' +[//]: # 'Info1' +[//]: # 'Info1' +[//]: # 'Example2' +[//]: # 'Example2' +[//]: # 'Example3' + +```svelte + + +
{ + e.preventDefault() + mutation.mutate({ title: title }) + }} +> + {#if mutation.error} +
mutation.reset()}>{mutation.error}
+ {/if} + (title = e.currentTarget.value)} + /> +
+ +
+``` + +[//]: # 'Example3' +[//]: # 'Example4' + +```ts +createMutation(() => ({ + mutationFn: addTodo, + onMutate: (variables, context) => { + // A mutation is about to happen! + + // Optionally return a result containing data to use when for example rolling back + return { id: 1 } + }, + onError: (error, variables, onMutateResult, context) => { + // An error happened! + console.log(`rolling back optimistic update with id ${onMutateResult.id}`) + }, + onSuccess: (data, variables, onMutateResult, context) => { + // Boom baby! + }, + onSettled: (data, error, variables, onMutateResult, context) => { + // Error or success... doesn't matter! + }, +})) +``` + +[//]: # 'Example4' +[//]: # 'Example5' + +```ts +createMutation(() => ({ + mutationFn: addTodo, + onSuccess: async () => { + console.log("I'm first!") + }, + onSettled: async () => { + console.log("I'm second!") + }, +})) +``` + +[//]: # 'Example5' +[//]: # 'Example6' + +```ts +const mutation = createMutation(() => ({ + mutationFn: addTodo, + onSuccess: (data, variables, onMutateResult, context) => { + // I will fire first + }, + onError: (error, variables, onMutateResult, context) => { + // I will fire first + }, + onSettled: (data, error, variables, onMutateResult, context) => { + // I will fire first + }, +})) + +mutation.mutate(todo, { + onSuccess: (data, variables, onMutateResult, context) => { + // I will fire second! + }, + onError: (error, variables, onMutateResult, context) => { + // I will fire second! + }, + onSettled: (data, error, variables, onMutateResult, context) => { + // I will fire second! + }, +}) +``` + +[//]: # 'Example6' +[//]: # 'Example7' + +```ts +const mutation = createMutation(() => ({ + mutationFn: addTodo, + onSuccess: (data, variables, onMutateResult, context) => { + // Will be called 3 times + }, +})) + +const todos = ['Todo 1', 'Todo 2', 'Todo 3'] +todos.forEach((todo) => { + mutation.mutate(todo, { + onSuccess: (data, variables, onMutateResult, context) => { + // Will execute only once, for the last mutation (Todo 3), + // regardless which mutation resolves first + }, + }) +}) +``` + +[//]: # 'Example7' +[//]: # 'Example8' + +```ts +const mutation = createMutation(() => ({ mutationFn: addTodo })) + +try { + const todo = await mutation.mutateAsync(todo) + console.log(todo) +} catch (error) { + console.error(error) +} finally { + console.log('done') +} +``` + +[//]: # 'Example8' +[//]: # 'Example9' + +```ts +const mutation = createMutation(() => ({ + mutationFn: addTodo, + retry: 3, +})) +``` + +[//]: # 'Example9' +[//]: # 'Example10' + +```ts +const queryClient = new QueryClient() + +// Define the "addTodo" mutation +queryClient.setMutationDefaults(['addTodo'], { + mutationFn: addTodo, + onMutate: async (variables, context) => { + // Cancel current queries for the todos list + await context.client.cancelQueries({ queryKey: ['todos'] }) + + // Create optimistic todo + const optimisticTodo = { id: uuid(), title: variables.title } + + // Add optimistic todo to todos list + context.client.setQueryData(['todos'], (old) => [...old, optimisticTodo]) + + // Return a result with the optimistic todo + return { optimisticTodo } + }, + onSuccess: (result, variables, onMutateResult, context) => { + // Replace optimistic todo in the todos list with the result + context.client.setQueryData(['todos'], (old) => + old.map((todo) => + todo.id === onMutateResult.optimisticTodo.id ? result : todo, + ), + ) + }, + onError: (error, variables, onMutateResult, context) => { + // Remove optimistic todo from the todos list + context.client.setQueryData(['todos'], (old) => + old.filter((todo) => todo.id !== onMutateResult.optimisticTodo.id), + ) + }, + retry: 3, +}) + +// Start mutation in some component: +const mutation = createMutation(() => ({ mutationKey: ['addTodo'] })) +mutation.mutate({ title: 'title' }) + +// If the mutation has been paused because the device is for example offline, +// Then the paused mutation can be dehydrated when the application quits: +const state = dehydrate(queryClient) + +// The mutation can then be hydrated again when the application is started: +hydrate(queryClient, state) + +// Resume the paused mutations: +queryClient.resumePausedMutations() +``` + +[//]: # 'Example10' +[//]: # 'PersistOfflineIntro' +[//]: # 'PersistOfflineIntro' +[//]: # 'Example11' +[//]: # 'Example11' +[//]: # 'OfflineExampleLink' +[//]: # 'OfflineExampleLink' +[//]: # 'ExampleScopes' + +```ts +const mutation = createMutation(() => ({ + mutationFn: addTodo, + scope: { + id: 'todo', + }, +})) +``` + +[//]: # 'ExampleScopes' +[//]: # 'Materials' +[//]: # 'Materials' diff --git a/docs/framework/svelte/guides/network-mode.md b/docs/framework/svelte/guides/network-mode.md new file mode 100644 index 00000000000..e773e73ff02 --- /dev/null +++ b/docs/framework/svelte/guides/network-mode.md @@ -0,0 +1,5 @@ +--- +id: network-mode +title: Network Mode +ref: docs/framework/react/guides/network-mode.md +--- diff --git a/docs/framework/svelte/guides/optimistic-updates.md b/docs/framework/svelte/guides/optimistic-updates.md new file mode 100644 index 00000000000..866e76ad117 --- /dev/null +++ b/docs/framework/svelte/guides/optimistic-updates.md @@ -0,0 +1,154 @@ +--- +id: optimistic-updates +title: Optimistic Updates +ref: docs/framework/react/guides/optimistic-updates.md +replace: + { + 'React': 'Svelte', + 'useMutation': 'createMutation', + 'hook': 'function', + 'useMutationState': 'createMutationState', + } +--- + +[//]: # 'ExampleUI1' + +```ts +const addTodoMutation = createMutation(() => ({ + mutationFn: (newTodo: string) => axios.post('/api/data', { text: newTodo }), + // make sure to _return_ the Promise from the query invalidation + // so that the mutation stays in `pending` state until the refetch is finished + onSettled: () => queryClient.invalidateQueries({ queryKey: ['todos'] }), +})) +``` + +[//]: # 'ExampleUI1' +[//]: # 'ExampleUI2' + +```svelte + +``` + +[//]: # 'ExampleUI2' +[//]: # 'ExampleUI3' + +```svelte +{#if addTodoMutation.isError} +
  • + {addTodoMutation.variables} + +
  • +{/if} +``` + +[//]: # 'ExampleUI3' +[//]: # 'ExampleUI4' + +```ts +// somewhere in your app +const mutation = createMutation(() => ({ + mutationFn: (newTodo: string) => axios.post('/api/data', { text: newTodo }), + onSettled: () => queryClient.invalidateQueries({ queryKey: ['todos'] }), + mutationKey: ['addTodo'], +})) + +// access variables somewhere else +const variables = useMutationState(() => ({ + filters: { mutationKey: ['addTodo'], status: 'pending' }, + select: (mutation) => mutation.state.variables, +})) +``` + +[//]: # 'ExampleUI4' +[//]: # 'Example' + +```ts +const queryClient = createQueryClient() + +createMutation(() => ({ + mutationFn: updateTodo, + // When mutate is called: + onMutate: async (newTodo, context) => { + // Cancel any outgoing refetches + // (so they don't overwrite our optimistic update) + await context.client.cancelQueries({ queryKey: ['todos'] }) + + // Snapshot the previous value + const previousTodos = context.client.getQueryData(['todos']) + + // Optimistically update to the new value + context.client.setQueryData(['todos'], (old) => [...old, newTodo]) + + // Return a result with the snapshotted value + return { previousTodos } + }, + // If the mutation fails, + // use the result returned from onMutate to roll back + onError: (err, newTodo, onMutateResult, context) => { + context.client.setQueryData(['todos'], onMutateResult.previousTodos) + }, + // Always refetch after error or success: + onSettled: (data, error, variables, onMutateResult, context) => + context.client.invalidateQueries({ queryKey: ['todos'] }), +})) +``` + +[//]: # 'Example' +[//]: # 'Example2' + +```ts +createMutation(() => ({ + mutationFn: updateTodo, + // When mutate is called: + onMutate: async (newTodo, context) => { + // Cancel any outgoing refetches + // (so they don't overwrite our optimistic update) + await context.client.cancelQueries({ queryKey: ['todos', newTodo.id] }) + + // Snapshot the previous value + const previousTodo = context.client.getQueryData(['todos', newTodo.id]) + + // Optimistically update to the new value + context.client.setQueryData(['todos', newTodo.id], newTodo) + + // Return a result with the previous and new todo + return { previousTodo, newTodo } + }, + // If the mutation fails, use the result we returned above + onError: (err, newTodo, onMutateResult, context) => { + context.client.setQueryData( + ['todos', onMutateResult.newTodo.id], + onMutateResult.previousTodo, + ) + }, + // Always refetch after error or success: + onSettled: (newTodo, error, variables, onMutateResult, context) => + context.client.invalidateQueries({ queryKey: ['todos', newTodo.id] }), +})) +``` + +[//]: # 'Example2' +[//]: # 'Example3' + +```ts +createMutation(() => ({ + mutationFn: updateTodo, + // ... + onSettled: async (newTodo, error, variables, onMutateResult, context) => { + if (error) { + // do something + } + }, +})) +``` + +[//]: # 'Example3' diff --git a/docs/framework/svelte/guides/queries.md b/docs/framework/svelte/guides/queries.md new file mode 100644 index 00000000000..52f4b865ff2 --- /dev/null +++ b/docs/framework/svelte/guides/queries.md @@ -0,0 +1,90 @@ +--- +id: queries +title: Queries +ref: docs/framework/react/guides/queries.md +replace: + { + 'React': 'Svelte', + 'react-query': 'svelte-query', + 'or custom hooks': '', + 'the `useQuery` hook': '`createQuery`', + 'useQuery': 'createQuery', + } +--- + +[//]: # 'Example' + +```svelte + +``` + +[//]: # 'Example' +[//]: # 'Example2' + +```ts +const todosQuery = createQuery(() => ({ + queryKey: ['todos'], + queryFn: fetchTodoList, +})) +``` + +[//]: # 'Example2' +[//]: # 'Example3' + +```svelte + + +
    + {#if todosQuery.isPending} +

    Loading...

    + {:else if todosQuery.isError} +

    Error: {todosQuery.error.message}

    + {:else if todosQuery.isSuccess} + {#each todosQuery.data as todo} +

    {todo.title}

    + {/each} + {/if} +
    +``` + +[//]: # 'Example3' + +If booleans aren't your thing, you can always use the `status` state as well: + +[//]: # 'Example4' + +```svelte + + +
    + {#if todosQuery.status === 'pending'} +

    Loading...

    + {:else if todosQuery.status === 'error'} +

    Error: {todosQuery.error.message}

    + {:else if todosQuery.status === 'success'} + {#each todosQuery.data as todo} +

    {todo.title}

    + {/each} + {/if} +
    +``` + +[//]: # 'Example4' +[//]: # 'Materials' +[//]: # 'Materials' diff --git a/docs/framework/svelte/guides/query-cancellation.md b/docs/framework/svelte/guides/query-cancellation.md new file mode 100644 index 00000000000..3b647dff5aa --- /dev/null +++ b/docs/framework/svelte/guides/query-cancellation.md @@ -0,0 +1,160 @@ +--- +id: query-cancellation +title: Query Cancellation +ref: docs/framework/react/guides/query-cancellation.md +replace: + { + '@tanstack/react-query': '@tanstack/svelte-query', + 'useQuery[(]': 'createQuery(() => ', + } +--- + +[//]: # 'Example' + +```ts +const todosQuery = createQuery(() => ({ + queryKey: ['todos'], + queryFn: async ({ signal }) => { + const todosResponse = await fetch('/todos', { + // Pass the signal to one fetch + signal, + }) + const todos = await todosResponse.json() + + const todoDetails = todos.map(async ({ details }) => { + const response = await fetch(details, { + // Or pass it to several + signal, + }) + return response.json() + }) + + return Promise.all(todoDetails) + }, +})) +``` + +[//]: # 'Example' +[//]: # 'Example2' + +```ts +import axios from 'axios' + +const todosQuery = createQuery(() => ({ + queryKey: ['todos'], + queryFn: ({ signal }) => + axios.get('/todos', { + // Pass the signal to `axios` + signal, + }), +})) +``` + +[//]: # 'Example2' +[//]: # 'Example3' + +```ts +import axios from 'axios' + +const todosQuery = createQuery(() => ({ + queryKey: ['todos'], + queryFn: ({ signal }) => { + // Create a new CancelToken source for this request + const CancelToken = axios.CancelToken + const source = CancelToken.source() + + const promise = axios.get('/todos', { + // Pass the source token to your request + cancelToken: source.token, + }) + + // Cancel the request if TanStack Query signals to abort + signal?.addEventListener('abort', () => { + source.cancel('Query was cancelled by TanStack Query') + }) + + return promise + }, +})) +``` + +[//]: # 'Example3' +[//]: # 'Example4' + +```ts +const todosQuery = createQuery(() => ({ + queryKey: ['todos'], + queryFn: ({ signal }) => { + return new Promise((resolve, reject) => { + var oReq = new XMLHttpRequest() + oReq.addEventListener('load', () => { + resolve(JSON.parse(oReq.responseText)) + }) + signal?.addEventListener('abort', () => { + oReq.abort() + reject() + }) + oReq.open('GET', '/todos') + oReq.send() + }) + }, +})) +``` + +[//]: # 'Example4' +[//]: # 'Example5' + +```ts +const client = new GraphQLClient(endpoint) + +const todosQuery = createQuery(() => ({ + queryKey: ['todos'], + queryFn: ({ signal }) => { + client.request({ document: query, signal }) + }, +})) +``` + +[//]: # 'Example5' +[//]: # 'Example6' + +```ts +const todosQuery = createQuery(() => ({ + queryKey: ['todos'], + queryFn: ({ signal }) => { + const client = new GraphQLClient(endpoint, { + signal, + }) + return client.request(query, variables) + }, +})) +``` + +[//]: # 'Example6' +[//]: # 'Example7' + +```svelte + + + +``` + +[//]: # 'Example7' +[//]: # 'Limitations' +[//]: # 'Limitations' From b765feaa8ea60f3fe7429fd38c76d84a426513ba Mon Sep 17 00:00:00 2001 From: Lucas Leung Date: Thu, 20 Aug 2026 17:47:58 +0800 Subject: [PATCH 2/5] FIX: some typo and mistake --- .../svelte/guides/invalidations-from-mutations.md | 2 -- docs/framework/svelte/guides/optimistic-updates.md | 9 ++------- docs/framework/svelte/guides/query-cancellation.md | 2 +- 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/docs/framework/svelte/guides/invalidations-from-mutations.md b/docs/framework/svelte/guides/invalidations-from-mutations.md index 44e316c1459..3e3c4bb4ee1 100644 --- a/docs/framework/svelte/guides/invalidations-from-mutations.md +++ b/docs/framework/svelte/guides/invalidations-from-mutations.md @@ -9,8 +9,6 @@ replace: 'useMutation[(]': 'createMutation(() => ', 'useMutation': 'createMutation', 'useQuery[(]': 'createQuery(() => ', - ', useQueryClient': ', QueryClient', - 'useQueryClient': 'new QueryClient', 'hook': 'function', } --- diff --git a/docs/framework/svelte/guides/optimistic-updates.md b/docs/framework/svelte/guides/optimistic-updates.md index 866e76ad117..cb76f7deb48 100644 --- a/docs/framework/svelte/guides/optimistic-updates.md +++ b/docs/framework/svelte/guides/optimistic-updates.md @@ -3,12 +3,7 @@ id: optimistic-updates title: Optimistic Updates ref: docs/framework/react/guides/optimistic-updates.md replace: - { - 'React': 'Svelte', - 'useMutation': 'createMutation', - 'hook': 'function', - 'useMutationState': 'createMutationState', - } + { 'React': 'Svelte', 'useMutation': 'createMutation', 'hook': 'function' } --- [//]: # 'ExampleUI1' @@ -72,7 +67,7 @@ const variables = useMutationState(() => ({ [//]: # 'Example' ```ts -const queryClient = createQueryClient() +const queryClient = useQueryClient() createMutation(() => ({ mutationFn: updateTodo, diff --git a/docs/framework/svelte/guides/query-cancellation.md b/docs/framework/svelte/guides/query-cancellation.md index 3b647dff5aa..b38323ca3d4 100644 --- a/docs/framework/svelte/guides/query-cancellation.md +++ b/docs/framework/svelte/guides/query-cancellation.md @@ -143,7 +143,7 @@ const todosQuery = createQuery(() => ({ }, })) - const queryClient = new QueryClient() + const queryClient = useQueryClient()