snapshot of https://github.com/TanStack/query.git (main, 2026-09-16; blobs > 131072B omitted)
This commit is contained in:
45
docs/framework/vue/guides/background-fetching-indicators.md
Normal file
45
docs/framework/vue/guides/background-fetching-indicators.md
Normal file
@@ -0,0 +1,45 @@
|
||||
---
|
||||
id: background-fetching-indicators
|
||||
title: Background Fetching Indicators
|
||||
ref: docs/framework/react/guides/background-fetching-indicators.md
|
||||
---
|
||||
|
||||
[//]: # 'Example'
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
import { useQuery } from '@tanstack/vue-query'
|
||||
|
||||
const { isPending, isFetching, isError, data, error } = useQuery({
|
||||
queryKey: ['todos'],
|
||||
queryFn: getTodos,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="isFetching">Refreshing...</div>
|
||||
<span v-if="isPending">Loading...</span>
|
||||
<span v-else-if="isError">Error: {{ error.message }}</span>
|
||||
<!-- We can assume by this point that `isSuccess === true` -->
|
||||
<ul v-else-if="data">
|
||||
<li v-for="todo in data" :key="todo.id">{{ todo.title }}</li>
|
||||
</ul>
|
||||
</template>
|
||||
```
|
||||
|
||||
[//]: # 'Example'
|
||||
[//]: # 'Example2'
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
import { useIsFetching } from '@tanstack/vue-query'
|
||||
|
||||
const isFetching = useIsFetching()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="isFetching">Queries are fetching in the background...</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
[//]: # 'Example2'
|
||||
5
docs/framework/vue/guides/caching.md
Normal file
5
docs/framework/vue/guides/caching.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
id: caching
|
||||
title: Caching Examples
|
||||
ref: docs/framework/react/guides/caching.md
|
||||
---
|
||||
72
docs/framework/vue/guides/custom-client.md
Normal file
72
docs/framework/vue/guides/custom-client.md
Normal file
@@ -0,0 +1,72 @@
|
||||
---
|
||||
id: custom-client
|
||||
title: Custom Client
|
||||
---
|
||||
|
||||
### Custom client
|
||||
|
||||
Vue Query allows providing custom `QueryClient` for Vue context.
|
||||
|
||||
It might be handy when you need to create `QueryClient` beforehand to integrate it with other libraries that do not have access to the Vue context.
|
||||
|
||||
For this reason, `VueQueryPlugin` accepts either `QueryClientConfig` or `QueryClient` as a plugin options.
|
||||
|
||||
If You provide `QueryClientConfig`, `QueryClient` instance will be created internally and provided to Vue context.
|
||||
|
||||
```tsx
|
||||
const vueQueryPluginOptions: VueQueryPluginOptions = {
|
||||
queryClientConfig: {
|
||||
defaultOptions: { queries: { staleTime: 3600 } },
|
||||
},
|
||||
}
|
||||
app.use(VueQueryPlugin, vueQueryPluginOptions)
|
||||
```
|
||||
|
||||
```tsx
|
||||
const myClient = new QueryClient(queryClientConfig)
|
||||
const vueQueryPluginOptions: VueQueryPluginOptions = {
|
||||
queryClient: myClient,
|
||||
}
|
||||
app.use(VueQueryPlugin, vueQueryPluginOptions)
|
||||
```
|
||||
|
||||
### Custom context key
|
||||
|
||||
You can also customize the key under which `QueryClient` will be accessible in Vue context. This can be useful is you want to avoid name clashing between multiple apps on the same page with Vue2.
|
||||
|
||||
It works both with default, and custom `QueryClient`
|
||||
|
||||
```tsx
|
||||
const vueQueryPluginOptions: VueQueryPluginOptions = {
|
||||
queryClientKey: 'Foo',
|
||||
}
|
||||
app.use(VueQueryPlugin, vueQueryPluginOptions)
|
||||
```
|
||||
|
||||
```tsx
|
||||
const myClient = new QueryClient(queryClientConfig)
|
||||
const vueQueryPluginOptions: VueQueryPluginOptions = {
|
||||
queryClient: myClient,
|
||||
queryClientKey: 'Foo',
|
||||
}
|
||||
app.use(VueQueryPlugin, vueQueryPluginOptions)
|
||||
```
|
||||
|
||||
To use the custom client key, You have to provide it as a query options
|
||||
|
||||
```js
|
||||
useQuery({
|
||||
queryKey: ['query1'],
|
||||
queryFn: fetcher,
|
||||
queryClientKey: 'foo',
|
||||
})
|
||||
```
|
||||
|
||||
Internally custom key will be combined with default query key as a suffix. But user do not have to worry about it.
|
||||
|
||||
```tsx
|
||||
const vueQueryPluginOptions: VueQueryPluginOptions = {
|
||||
queryClientKey: 'Foo',
|
||||
}
|
||||
app.use(VueQueryPlugin, vueQueryPluginOptions) // -> VUE_QUERY_CLIENT:Foo
|
||||
```
|
||||
32
docs/framework/vue/guides/default-query-function.md
Normal file
32
docs/framework/vue/guides/default-query-function.md
Normal file
@@ -0,0 +1,32 @@
|
||||
---
|
||||
id: default-query-function
|
||||
title: Default Query Function
|
||||
ref: docs/framework/react/guides/default-query-function.md
|
||||
---
|
||||
|
||||
[//]: # 'Example'
|
||||
|
||||
```tsx
|
||||
// Define a default query function that will receive the query key
|
||||
const defaultQueryFn = async ({ queryKey }) => {
|
||||
const { data } = await axios.get(
|
||||
`https://jsonplaceholder.typicode.com${queryKey[0]}`,
|
||||
)
|
||||
return data
|
||||
}
|
||||
|
||||
// provide the default query function to your app with defaultOptions
|
||||
const vueQueryPluginOptions: VueQueryPluginOptions = {
|
||||
queryClientConfig: {
|
||||
defaultOptions: { queries: { queryFn: defaultQueryFn } },
|
||||
},
|
||||
}
|
||||
app.use(VueQueryPlugin, vueQueryPluginOptions)
|
||||
|
||||
// All you have to do now is pass a key!
|
||||
const { status, data, error, isFetching } = useQuery({
|
||||
queryKey: [`/posts/${postId}`],
|
||||
})
|
||||
```
|
||||
|
||||
[//]: # 'Example'
|
||||
55
docs/framework/vue/guides/dependent-queries.md
Normal file
55
docs/framework/vue/guides/dependent-queries.md
Normal file
@@ -0,0 +1,55 @@
|
||||
---
|
||||
id: dependent-queries
|
||||
title: Dependent Queries
|
||||
ref: docs/framework/react/guides/dependent-queries.md
|
||||
---
|
||||
|
||||
[//]: # 'Example'
|
||||
|
||||
```js
|
||||
// Get the user
|
||||
const { data: user } = useQuery({
|
||||
queryKey: ['user', email],
|
||||
queryFn: () => getUserByEmail(email.value),
|
||||
})
|
||||
|
||||
const userId = computed(() => user.value?.id)
|
||||
const enabled = computed(() => !!user.value?.id)
|
||||
|
||||
// Then get the user's projects
|
||||
const { isIdle, data: projects } = useQuery({
|
||||
queryKey: ['projects', userId],
|
||||
queryFn: () => getProjectsByUser(userId.value),
|
||||
enabled, // The query will not execute until `enabled == true`
|
||||
})
|
||||
```
|
||||
|
||||
[//]: # 'Example'
|
||||
[//]: # 'Example2'
|
||||
|
||||
```tsx
|
||||
// Get the users ids
|
||||
const { data: userIds } = useQuery({
|
||||
queryKey: ['users'],
|
||||
queryFn: getUsersData,
|
||||
select: (users) => users.map((user) => user.id),
|
||||
})
|
||||
|
||||
const queries = computed(() => {
|
||||
return userIds.value.length
|
||||
? userIds.value.map((id) => {
|
||||
return {
|
||||
queryKey: ['messages', id],
|
||||
queryFn: () => getMessagesByUsers(id),
|
||||
}
|
||||
})
|
||||
: []
|
||||
})
|
||||
|
||||
// Then get the users messages
|
||||
const usersMessages = useQueries({
|
||||
queries, // if userIds.value is undefined or has no items, an empty array will be returned
|
||||
})
|
||||
```
|
||||
|
||||
[//]: # 'Example2'
|
||||
79
docs/framework/vue/guides/disabling-queries.md
Normal file
79
docs/framework/vue/guides/disabling-queries.md
Normal file
@@ -0,0 +1,79 @@
|
||||
---
|
||||
id: disabling-queries
|
||||
title: Disabling/Pausing Queries
|
||||
ref: docs/framework/react/guides/disabling-queries.md
|
||||
---
|
||||
|
||||
[//]: # 'Example'
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
import { useQuery } from '@tanstack/vue-query'
|
||||
|
||||
const { isLoading, isError, data, error, refetch, isFetching } = useQuery({
|
||||
queryKey: ['todos'],
|
||||
queryFn: fetchTodoList,
|
||||
enabled: false,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<button @click="refetch()">Fetch Todos</button>
|
||||
<span v-if="isLoading">Loading...</span>
|
||||
<span v-else-if="isError">Error: {{ error?.message }}</span>
|
||||
<div v-else-if="data">
|
||||
<span v-if="isFetching">Fetching...</span>
|
||||
<ul>
|
||||
<li v-for="todo in data" :key="todo.id">{{ todo.title }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<span v-else>Not ready...</span>
|
||||
</template>
|
||||
```
|
||||
|
||||
[//]: # 'Example'
|
||||
[//]: # 'Example2'
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
import { useQuery } from '@tanstack/vue-query'
|
||||
|
||||
const filter = ref('')
|
||||
const isEnabled = computed(() => !!filter.value)
|
||||
const { data } = useQuery({
|
||||
queryKey: ['todos', filter],
|
||||
queryFn: () => fetchTodos(filter),
|
||||
// ⬇️ disabled as long as the filter is empty
|
||||
enabled: isEnabled,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<span v-if="data">Filter was set and data is here!</span>
|
||||
</template>
|
||||
```
|
||||
|
||||
[//]: # 'Example2'
|
||||
[//]: # 'Example3'
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
import { useQuery, skipToken } from '@tanstack/vue-query'
|
||||
|
||||
const filter = ref('')
|
||||
const queryFn = computed(() =>
|
||||
!!filter.value ? () => fetchTodos(filter) : skipToken,
|
||||
)
|
||||
const { data } = useQuery({
|
||||
queryKey: ['todos', filter],
|
||||
// ⬇️ disabled as long as the filter is undefined or empty
|
||||
queryFn: queryFn,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<span v-if="data">Filter was set and data is here!</span>
|
||||
</template>
|
||||
```
|
||||
|
||||
[//]: # 'Example3'
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
id: does-this-replace-client-state
|
||||
title: Does Vue Query replace Vuex, Pinia or other global state managers?
|
||||
ref: docs/framework/react/guides/does-this-replace-client-state.md
|
||||
replace: { 'Redux, MobX': 'Vuex, Pinia' }
|
||||
---
|
||||
5
docs/framework/vue/guides/filters.md
Normal file
5
docs/framework/vue/guides/filters.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
id: filters
|
||||
title: Filters
|
||||
ref: docs/framework/react/guides/filters.md
|
||||
---
|
||||
8
docs/framework/vue/guides/important-defaults.md
Normal file
8
docs/framework/vue/guides/important-defaults.md
Normal file
@@ -0,0 +1,8 @@
|
||||
---
|
||||
id: important-defaults
|
||||
title: Important Defaults
|
||||
ref: docs/framework/react/guides/important-defaults.md
|
||||
---
|
||||
|
||||
[//]: # 'Materials'
|
||||
[//]: # 'Materials'
|
||||
57
docs/framework/vue/guides/infinite-queries.md
Normal file
57
docs/framework/vue/guides/infinite-queries.md
Normal file
@@ -0,0 +1,57 @@
|
||||
---
|
||||
id: infinite-queries
|
||||
title: Infinite Queries
|
||||
ref: docs/framework/react/guides/infinite-queries.md
|
||||
---
|
||||
|
||||
[//]: # 'Example'
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
import { useInfiniteQuery } from '@tanstack/vue-query'
|
||||
|
||||
const fetchProjects = async ({ pageParam }) => {
|
||||
const res = await fetch('/api/projects?cursor=' + pageParam)
|
||||
return res.json()
|
||||
}
|
||||
|
||||
const {
|
||||
data,
|
||||
error,
|
||||
fetchNextPage,
|
||||
hasNextPage,
|
||||
isFetching,
|
||||
isFetchingNextPage,
|
||||
isPending,
|
||||
isError,
|
||||
} = useInfiniteQuery({
|
||||
queryKey: ['projects'],
|
||||
queryFn: fetchProjects,
|
||||
initialPageParam: 0,
|
||||
getNextPageParam: (lastPage, pages) => lastPage.nextCursor,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<span v-if="isPending">Loading...</span>
|
||||
<span v-else-if="isError">Error: {{ error.message }}</span>
|
||||
<div v-else-if="data">
|
||||
<span v-if="isFetching && !isFetchingNextPage">Fetching...</span>
|
||||
<ul v-for="(group, index) in data.pages" :key="index">
|
||||
<li v-for="project in group.projects" :key="project.id">
|
||||
{{ project.name }}
|
||||
</li>
|
||||
</ul>
|
||||
<button
|
||||
@click="() => fetchNextPage()"
|
||||
:disabled="!hasNextPage || isFetchingNextPage"
|
||||
>
|
||||
<span v-if="isFetchingNextPage">Loading more...</span>
|
||||
<span v-else-if="hasNextPage">Load More</span>
|
||||
<span v-else>Nothing more to load</span>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
[//]: # 'Example'
|
||||
8
docs/framework/vue/guides/initial-query-data.md
Normal file
8
docs/framework/vue/guides/initial-query-data.md
Normal file
@@ -0,0 +1,8 @@
|
||||
---
|
||||
id: initial-query-data
|
||||
title: Initial Query Data
|
||||
ref: docs/framework/react/guides/initial-query-data.md
|
||||
---
|
||||
|
||||
[//]: # 'Materials'
|
||||
[//]: # 'Materials'
|
||||
24
docs/framework/vue/guides/invalidations-from-mutations.md
Normal file
24
docs/framework/vue/guides/invalidations-from-mutations.md
Normal file
@@ -0,0 +1,24 @@
|
||||
---
|
||||
id: invalidations-from-mutations
|
||||
title: Invalidations from Mutations
|
||||
ref: docs/framework/react/guides/invalidations-from-mutations.md
|
||||
---
|
||||
|
||||
[//]: # 'Example2'
|
||||
|
||||
```tsx
|
||||
import { useMutation, useQueryClient } from '@tanstack/vue-query'
|
||||
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
// When this mutation succeeds, invalidate any queries with the `todos` or `reminders` query key
|
||||
const mutation = useMutation({
|
||||
mutationFn: addTodo,
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['todos'] })
|
||||
queryClient.invalidateQueries({ queryKey: ['reminders'] })
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
[//]: # 'Example2'
|
||||
41
docs/framework/vue/guides/migrating-to-v5.md
Normal file
41
docs/framework/vue/guides/migrating-to-v5.md
Normal file
@@ -0,0 +1,41 @@
|
||||
---
|
||||
id: migrating-to-tanstack-query-5
|
||||
title: Migrating to TanStack Query v5
|
||||
ref: docs/framework/react/guides/migrating-to-v5.md
|
||||
---
|
||||
|
||||
[//]: # 'FrameworkSpecificBreakingChanges'
|
||||
|
||||
## Vue Query Breaking Changes
|
||||
|
||||
### `useQueries` composable returns `ref` instead of `reactive`
|
||||
|
||||
To fix compatibility with Vue 2, `useQueries` composable now returns `queries` array wrapped in `ref`.
|
||||
Previously `reactive` was returned which led to multiple problems:
|
||||
|
||||
- User could spread return value loosing reactivity.
|
||||
- `readonly` wrapper used for return value was breaking Vue 2 reactivity detection mechanism. This was a silent issue in Vue 2.6, but appeared as error in Vue 2.7.
|
||||
- Vue 2 does not support arrays as a root value of `reactive`.
|
||||
|
||||
With this change all of those issues are fixed.
|
||||
|
||||
Also this aligns `useQueries` with other composables which return all of the values as `refs`.
|
||||
|
||||
### Vue v3.3 is now required
|
||||
|
||||
To be able to provide new features following Vue releases, we now require Vue 3 to be at least in v3.3 version.
|
||||
Requirements for Vue 2.x remain unchanged.
|
||||
|
||||
[//]: # 'FrameworkSpecificBreakingChanges'
|
||||
[//]: # 'FrameworkSpecificNewFeatures'
|
||||
|
||||
### Ability to run `vue-query` composables in `injectionContext`
|
||||
|
||||
Previously `vue-query` composables could be run only within `setup` function of the component.
|
||||
We had an escape hatch in place to allow those hooks to be run anywhere if user would provide `queryClient` as a composable option.
|
||||
|
||||
Now you can use `vue-query` composables in any function that supports `injectionContext`. Ex. router navigation guards.
|
||||
When using this new feature, make sure that `vue-query` composable is running within `effectScope`. Otherwise it might lead to memory leaks.
|
||||
We have added `dev-only` warnings to inform users about potential misusage.
|
||||
|
||||
[//]: # 'FrameworkSpecificNewFeatures'
|
||||
97
docs/framework/vue/guides/mutations.md
Normal file
97
docs/framework/vue/guides/mutations.md
Normal file
@@ -0,0 +1,97 @@
|
||||
---
|
||||
id: mutations
|
||||
title: Mutations
|
||||
ref: docs/framework/react/guides/mutations.md
|
||||
---
|
||||
|
||||
[//]: # 'Example'
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
import { useMutation } from '@tanstack/vue-query'
|
||||
|
||||
const { isPending, isError, error, isSuccess, mutate } = useMutation({
|
||||
mutationFn: (newTodo) => axios.post('/todos', newTodo),
|
||||
})
|
||||
|
||||
function addTodo() {
|
||||
mutate({ id: new Date(), title: 'Do Laundry' })
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<span v-if="isPending">Adding todo...</span>
|
||||
<span v-else-if="isError">An error occurred: {{ error.message }}</span>
|
||||
<span v-else-if="isSuccess">Todo added!</span>
|
||||
<button @click="addTodo">Create Todo</button>
|
||||
</template>
|
||||
```
|
||||
|
||||
[//]: # 'Example'
|
||||
[//]: # 'Info1'
|
||||
[//]: # 'Info1'
|
||||
[//]: # 'Example2'
|
||||
[//]: # 'Example2'
|
||||
[//]: # 'Example3'
|
||||
|
||||
```vue
|
||||
<script>
|
||||
import { useMutation } from '@tanstack/vue-query'
|
||||
|
||||
const { error, mutate, reset } = useMutation({
|
||||
mutationFn: (newTodo) => axios.post('/todos', newTodo),
|
||||
})
|
||||
|
||||
function addTodo() {
|
||||
mutate({ id: new Date(), title: 'Do Laundry' })
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<span v-else-if="error">
|
||||
<span>An error occurred: {{ error.message }}</span>
|
||||
<button @click="reset">Reset error</button>
|
||||
</span>
|
||||
<button @click="addTodo">Create Todo</button>
|
||||
</template>
|
||||
```
|
||||
|
||||
[//]: # 'Example3'
|
||||
[//]: # 'Example11'
|
||||
|
||||
```js
|
||||
const client = new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
gcTime: 1000 * 60 * 60 * 24, // 24 hours
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
// we need a default mutation function so that paused mutations can resume after a page reload
|
||||
queryClient.setMutationDefaults({
|
||||
mutationKey: ['todos'],
|
||||
mutationFn: ({ id, data }) => {
|
||||
return api.updateTodo(id, data)
|
||||
},
|
||||
})
|
||||
|
||||
const vueQueryOptions: VueQueryPluginOptions = {
|
||||
queryClient: client,
|
||||
clientPersister: (queryClient) => {
|
||||
return persistQueryClient({
|
||||
queryClient,
|
||||
persister: createAsyncStoragePersister({ storage: localStorage }),
|
||||
})
|
||||
},
|
||||
clientPersisterOnSuccess: (queryClient) => {
|
||||
queryClient.resumePausedMutations()
|
||||
},
|
||||
}
|
||||
|
||||
createApp(App).use(VueQueryPlugin, vueQueryOptions).mount('#app')
|
||||
```
|
||||
|
||||
[//]: # 'Example11'
|
||||
[//]: # 'Materials'
|
||||
[//]: # 'Materials'
|
||||
5
docs/framework/vue/guides/network-mode.md
Normal file
5
docs/framework/vue/guides/network-mode.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
id: network-mode
|
||||
title: Network Mode
|
||||
ref: docs/framework/react/guides/network-mode.md
|
||||
---
|
||||
6
docs/framework/vue/guides/optimistic-updates.md
Normal file
6
docs/framework/vue/guides/optimistic-updates.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
id: optimistic-updates
|
||||
title: Optimistic Updates
|
||||
ref: docs/framework/react/guides/optimistic-updates.md
|
||||
replace: { 'React': 'Vue' }
|
||||
---
|
||||
53
docs/framework/vue/guides/paginated-queries.md
Normal file
53
docs/framework/vue/guides/paginated-queries.md
Normal file
@@ -0,0 +1,53 @@
|
||||
---
|
||||
id: paginated-queries
|
||||
title: Paginated / Lagged Queries
|
||||
ref: docs/framework/react/guides/paginated-queries.md
|
||||
---
|
||||
|
||||
[//]: # 'Example2'
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
import { ref, Ref } from 'vue'
|
||||
import { useQuery, keepPreviousData } from '@tanstack/vue-query'
|
||||
|
||||
const fetcher = (page: Ref<number>) =>
|
||||
fetch(
|
||||
`https://jsonplaceholder.typicode.com/posts?_page=${page.value}&_limit=10`,
|
||||
).then((response) => response.json())
|
||||
|
||||
const page = ref(1)
|
||||
const { isPending, isError, data, error, isFetching, isPlaceholderData } =
|
||||
useQuery({
|
||||
queryKey: ['projects', page],
|
||||
queryFn: () => fetcher(page),
|
||||
placeholderData: keepPreviousData,
|
||||
})
|
||||
const prevPage = () => {
|
||||
page.value = Math.max(page.value - 1, 1)
|
||||
}
|
||||
const nextPage = () => {
|
||||
if (!isPlaceholderData.value) {
|
||||
page.value = page.value + 1
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<h1>Posts</h1>
|
||||
<p>Current Page: {{ page }} | Previous data: {{ isPlaceholderData }}</p>
|
||||
<button @click="prevPage">Prev Page</button>
|
||||
<button @click="nextPage">Next Page</button>
|
||||
<div v-if="isPending">Loading...</div>
|
||||
<div v-else-if="isError">An error has occurred: {{ error }}</div>
|
||||
<div v-else-if="data">
|
||||
<ul>
|
||||
<li v-for="item in data" :key="item.id">
|
||||
{{ item.title }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
[//]: # 'Example2'
|
||||
38
docs/framework/vue/guides/parallel-queries.md
Normal file
38
docs/framework/vue/guides/parallel-queries.md
Normal file
@@ -0,0 +1,38 @@
|
||||
---
|
||||
id: parallel-queries
|
||||
title: Parallel Queries
|
||||
ref: docs/framework/react/guides/parallel-queries.md
|
||||
---
|
||||
|
||||
[//]: # 'Example'
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
// The following queries will execute in parallel
|
||||
const usersQuery = useQuery({ queryKey: ['users'], queryFn: fetchUsers })
|
||||
const teamsQuery = useQuery({ queryKey: ['teams'], queryFn: fetchTeams })
|
||||
const projectsQuery = useQuery({
|
||||
queryKey: ['projects'],
|
||||
queryFn: fetchProjects,
|
||||
})
|
||||
</script>
|
||||
```
|
||||
|
||||
[//]: # 'Example'
|
||||
[//]: # 'Info'
|
||||
[//]: # 'Info'
|
||||
[//]: # 'Example2'
|
||||
|
||||
```js
|
||||
const users = computed(...)
|
||||
const queries = computed(() => users.value.map(user => {
|
||||
return {
|
||||
queryKey: ['user', user.id],
|
||||
queryFn: () => fetchUserById(user.id),
|
||||
}
|
||||
})
|
||||
);
|
||||
const userQueries = useQueries({queries: queries})
|
||||
```
|
||||
|
||||
[//]: # 'Example2'
|
||||
38
docs/framework/vue/guides/placeholder-query-data.md
Normal file
38
docs/framework/vue/guides/placeholder-query-data.md
Normal file
@@ -0,0 +1,38 @@
|
||||
---
|
||||
id: placeholder-query-data
|
||||
title: Placeholder Query Data
|
||||
ref: docs/framework/react/guides/placeholder-query-data.md
|
||||
---
|
||||
|
||||
[//]: # 'ExampleValue'
|
||||
|
||||
```tsx
|
||||
const result = useQuery({
|
||||
queryKey: ['todos'],
|
||||
queryFn: () => fetch('/todos'),
|
||||
placeholderData: placeholderTodos,
|
||||
})
|
||||
```
|
||||
|
||||
[//]: # 'ExampleValue'
|
||||
[//]: # 'Memoization'
|
||||
[//]: # 'Memoization'
|
||||
[//]: # 'ExampleCache'
|
||||
|
||||
```tsx
|
||||
const result = useQuery({
|
||||
queryKey: ['blogPost', blogPostId],
|
||||
queryFn: () => fetch(`/blogPosts/${blogPostId}`),
|
||||
placeholderData: () => {
|
||||
// Use the smaller/preview version of the blogPost from the 'blogPosts'
|
||||
// query as the placeholder data for this blogPost query
|
||||
return queryClient
|
||||
.getQueryData(['blogPosts'])
|
||||
?.find((d) => d.id === blogPostId)
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
[//]: # 'ExampleCache'
|
||||
[//]: # 'Materials'
|
||||
[//]: # 'Materials'
|
||||
9
docs/framework/vue/guides/polling.md
Normal file
9
docs/framework/vue/guides/polling.md
Normal file
@@ -0,0 +1,9 @@
|
||||
---
|
||||
id: polling
|
||||
title: Polling
|
||||
ref: docs/framework/react/guides/polling.md
|
||||
replace: { '@tanstack/react-query': '@tanstack/vue-query' }
|
||||
---
|
||||
|
||||
[//]: # 'ReactNative'
|
||||
[//]: # 'ReactNative'
|
||||
57
docs/framework/vue/guides/prefetching.md
Normal file
57
docs/framework/vue/guides/prefetching.md
Normal file
@@ -0,0 +1,57 @@
|
||||
---
|
||||
id: prefetching
|
||||
title: Prefetching
|
||||
---
|
||||
|
||||
If you're lucky enough, you may know enough about what your users will do to be able to prefetch the data they need before it's needed. If this is the case, use `queryClient.query` or `queryClient.infiniteQuery` to warm the cache ahead of time:
|
||||
|
||||
[//]: # 'ExamplePrefetching'
|
||||
|
||||
```tsx
|
||||
import { noop } from '@tanstack/vue-query'
|
||||
|
||||
const prefetchTodos = async () => {
|
||||
// The results of this query will be cached like a normal query
|
||||
await queryClient
|
||||
.query({
|
||||
queryKey: ['todos'],
|
||||
queryFn: fetchTodos,
|
||||
})
|
||||
.catch(noop)
|
||||
}
|
||||
```
|
||||
|
||||
[//]: # 'ExamplePrefetching'
|
||||
|
||||
- If **fresh** data for this query is already in the cache, the data will not be fetched
|
||||
- If a `staleTime` is passed e.g. `queryClient.query({ queryKey: ['todos'], queryFn: fn, staleTime: 5000 })` and the data is older than the specified `staleTime`, the query will be fetched
|
||||
- As `useQuery` will retry fetches and handle errors, you can use `void` to ignore the promise from `query` and `.catch(noop)` to ignore errors.
|
||||
- If you want to always return cached data when it exists, use `staleTime: 'static'`
|
||||
- If no instances of `useQuery` appear for a prefetched query, it will be deleted and garbage collected after the time specified in `gcTime`.
|
||||
|
||||
## Prefetching Infinite Queries
|
||||
|
||||
Infinite Queries can be prefetched like regular Queries. Per default, only the first page of the Query will be prefetched and will be stored under the given QueryKey. If you want to prefetch more than one page, you can use the `pages` option, in which case you also have to provide a `getNextPageParam` function:
|
||||
|
||||
[//]: # 'ExampleInfiniteQuery'
|
||||
|
||||
```tsx
|
||||
import { noop } from '@tanstack/vue-query'
|
||||
|
||||
const prefetchProjects = async () => {
|
||||
// The results of this query will be cached like a normal query
|
||||
await queryClient
|
||||
.infiniteQuery({
|
||||
queryKey: ['projects'],
|
||||
queryFn: fetchProjects,
|
||||
initialPageParam: 0,
|
||||
getNextPageParam: (lastPage, pages) => lastPage.nextCursor,
|
||||
pages: 3, // prefetch the first 3 pages
|
||||
})
|
||||
.catch(noop)
|
||||
}
|
||||
```
|
||||
|
||||
[//]: # 'ExampleInfiniteQuery'
|
||||
|
||||
The above code will try to prefetch 3 pages in order, and `getNextPageParam` will be executed for each page to determine the next page to prefetch. If `getNextPageParam` returns `undefined`, the prefetching will stop.
|
||||
63
docs/framework/vue/guides/queries.md
Normal file
63
docs/framework/vue/guides/queries.md
Normal file
@@ -0,0 +1,63 @@
|
||||
---
|
||||
id: queries
|
||||
title: Queries
|
||||
ref: docs/framework/react/guides/queries.md
|
||||
---
|
||||
|
||||
[//]: # 'Example'
|
||||
|
||||
```ts
|
||||
import { useQuery } from '@tanstack/vue-query'
|
||||
|
||||
const result = useQuery({ queryKey: ['todos'], queryFn: fetchTodoList })
|
||||
```
|
||||
|
||||
[//]: # 'Example'
|
||||
[//]: # 'Example3'
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
import { useQuery } from '@tanstack/vue-query'
|
||||
|
||||
const { isPending, isError, data, error } = useQuery({
|
||||
queryKey: ['todos'],
|
||||
queryFn: fetchTodoList,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<span v-if="isPending">Loading...</span>
|
||||
<span v-else-if="isError">Error: {{ error.message }}</span>
|
||||
<!-- We can assume by this point that `isSuccess === true` -->
|
||||
<ul v-else-if="data">
|
||||
<li v-for="todo in data" :key="todo.id">{{ todo.title }}</li>
|
||||
</ul>
|
||||
</template>
|
||||
```
|
||||
|
||||
[//]: # 'Example3'
|
||||
[//]: # 'Example4'
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
import { useQuery } from '@tanstack/vue-query'
|
||||
|
||||
const { status, data, error } = useQuery({
|
||||
queryKey: ['todos'],
|
||||
queryFn: fetchTodoList,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<span v-if="status === 'pending'">Loading...</span>
|
||||
<span v-else-if="status === 'error'">Error: {{ error.message }}</span>
|
||||
<!-- also status === 'success', but "else" logic works, too -->
|
||||
<ul v-else-if="data">
|
||||
<li v-for="todo in data" :key="todo.id">{{ todo.title }}</li>
|
||||
</ul>
|
||||
</template>
|
||||
```
|
||||
|
||||
[//]: # 'Example4'
|
||||
[//]: # 'Materials'
|
||||
[//]: # 'Materials'
|
||||
25
docs/framework/vue/guides/query-cancellation.md
Normal file
25
docs/framework/vue/guides/query-cancellation.md
Normal file
@@ -0,0 +1,25 @@
|
||||
---
|
||||
id: query-cancellation
|
||||
title: Query Cancellation
|
||||
ref: docs/framework/react/guides/query-cancellation.md
|
||||
---
|
||||
|
||||
[//]: # 'Example7'
|
||||
|
||||
```ts
|
||||
const query = useQuery({
|
||||
queryKey: ['todos'],
|
||||
queryFn: async ({ signal }) => {
|
||||
const resp = await fetch('/todos', { signal })
|
||||
return resp.json()
|
||||
},
|
||||
})
|
||||
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
function onButtonClick() {
|
||||
queryClient.cancelQueries({ queryKey: ['todos'] })
|
||||
}
|
||||
```
|
||||
|
||||
[//]: # 'Example7'
|
||||
22
docs/framework/vue/guides/query-functions.md
Normal file
22
docs/framework/vue/guides/query-functions.md
Normal file
@@ -0,0 +1,22 @@
|
||||
---
|
||||
id: query-functions
|
||||
title: Query Functions
|
||||
ref: docs/framework/react/guides/query-functions.md
|
||||
---
|
||||
|
||||
[//]: # 'Example4'
|
||||
|
||||
```js
|
||||
const result = useQuery({
|
||||
queryKey: ['todos', { status, page }],
|
||||
queryFn: fetchTodoList,
|
||||
})
|
||||
|
||||
// Access the key, status and page variables in your query function!
|
||||
function fetchTodoList({ queryKey }) {
|
||||
const [_key, { status, page }] = queryKey
|
||||
return new Promise()
|
||||
}
|
||||
```
|
||||
|
||||
[//]: # 'Example4'
|
||||
6
docs/framework/vue/guides/query-invalidation.md
Normal file
6
docs/framework/vue/guides/query-invalidation.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
id: query-invalidation
|
||||
title: Query Invalidation
|
||||
ref: docs/framework/react/guides/query-invalidation.md
|
||||
replace: { 'react-query': 'vue-query' }
|
||||
---
|
||||
21
docs/framework/vue/guides/query-keys.md
Normal file
21
docs/framework/vue/guides/query-keys.md
Normal file
@@ -0,0 +1,21 @@
|
||||
---
|
||||
id: query-keys
|
||||
title: Query Keys
|
||||
ref: docs/framework/react/guides/query-keys.md
|
||||
---
|
||||
|
||||
[//]: # 'Example5'
|
||||
|
||||
```ts
|
||||
import type { Ref } from 'vue'
|
||||
|
||||
function useTodos(todoId: Ref<string>) {
|
||||
const queryKey = ['todos', todoId]
|
||||
return useQuery({
|
||||
queryKey,
|
||||
queryFn: () => fetchTodoById(todoId.value),
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
[//]: # 'Example5'
|
||||
6
docs/framework/vue/guides/query-options.md
Normal file
6
docs/framework/vue/guides/query-options.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
id: query-options
|
||||
title: Query Options
|
||||
ref: docs/framework/react/guides/query-options.md
|
||||
replace: { '@tanstack/react-query': '@tanstack/vue-query' }
|
||||
---
|
||||
39
docs/framework/vue/guides/query-retries.md
Normal file
39
docs/framework/vue/guides/query-retries.md
Normal file
@@ -0,0 +1,39 @@
|
||||
---
|
||||
id: query-retries
|
||||
title: Query Retries
|
||||
ref: docs/framework/react/guides/query-retries.md
|
||||
replace: { 'Provider': 'Plugin' }
|
||||
---
|
||||
|
||||
[//]: # 'Example'
|
||||
|
||||
```tsx
|
||||
import { useQuery } from '@tanstack/vue-query'
|
||||
|
||||
// Make a specific query retry a certain number of times
|
||||
const result = useQuery({
|
||||
queryKey: ['todos', 1],
|
||||
queryFn: fetchTodoListPage,
|
||||
retry: 10, // Will retry failed requests 10 times before displaying an error
|
||||
})
|
||||
```
|
||||
|
||||
[//]: # 'Example'
|
||||
[//]: # 'Example2'
|
||||
|
||||
```ts
|
||||
import { VueQueryPlugin } from '@tanstack/vue-query'
|
||||
|
||||
const vueQueryPluginOptions = {
|
||||
queryClientConfig: {
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
app.use(VueQueryPlugin, vueQueryPluginOptions)
|
||||
```
|
||||
|
||||
[//]: # 'Example2'
|
||||
5
docs/framework/vue/guides/scroll-restoration.md
Normal file
5
docs/framework/vue/guides/scroll-restoration.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
id: scroll-restoration
|
||||
title: Scroll Restoration
|
||||
ref: docs/framework/react/guides/scroll-restoration.md
|
||||
---
|
||||
278
docs/framework/vue/guides/ssr.md
Normal file
278
docs/framework/vue/guides/ssr.md
Normal file
@@ -0,0 +1,278 @@
|
||||
---
|
||||
id: ssr
|
||||
title: SSR
|
||||
---
|
||||
|
||||
Vue Query supports prefetching multiple queries on the server and then _dehydrating_ those queries to the queryClient. This means the server can prerender markup that is immediately available on page load and as soon as JS is available, Vue Query can upgrade or _hydrate_ those queries with the full functionality of the library. This includes refetching those queries on the client if they have become stale since the time they were rendered on the server.
|
||||
|
||||
## Using Nuxt.js
|
||||
|
||||
### Nuxt 3
|
||||
|
||||
First create `vue-query.ts` file in your `plugins` directory with the following content:
|
||||
|
||||
```ts
|
||||
import type {
|
||||
DehydratedState,
|
||||
VueQueryPluginOptions,
|
||||
} from '@tanstack/vue-query'
|
||||
import {
|
||||
VueQueryPlugin,
|
||||
QueryClient,
|
||||
hydrate,
|
||||
dehydrate,
|
||||
} from '@tanstack/vue-query'
|
||||
// Nuxt 3 app aliases
|
||||
import { defineNuxtPlugin, useState } from '#imports'
|
||||
|
||||
export default defineNuxtPlugin((nuxt) => {
|
||||
const vueQueryState = useState<DehydratedState | null>('vue-query')
|
||||
|
||||
// Modify your Vue Query global settings here
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: { queries: { staleTime: 5000 } },
|
||||
})
|
||||
const options: VueQueryPluginOptions = { queryClient }
|
||||
|
||||
nuxt.vueApp.use(VueQueryPlugin, options)
|
||||
|
||||
if (import.meta.server) {
|
||||
nuxt.hooks.hook('app:rendered', () => {
|
||||
vueQueryState.value = dehydrate(queryClient)
|
||||
})
|
||||
}
|
||||
|
||||
if (import.meta.client && vueQueryState.value !== null) {
|
||||
hydrate(queryClient, vueQueryState.value)
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
Now you are ready to prefetch some data in your pages with `onServerPrefetch`.
|
||||
|
||||
- Prefetch all the queries that you need with `queryClient.query`, `queryClient.infiniteQuery`, or `suspense`
|
||||
|
||||
```ts
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
const queryClient = useQueryClient()
|
||||
const { data } = useQuery({
|
||||
queryKey: ['test'],
|
||||
queryFn: fetcher,
|
||||
})
|
||||
|
||||
onServerPrefetch(async () => {
|
||||
await suspense()
|
||||
})
|
||||
|
||||
return { data }
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
### Nuxt 2
|
||||
|
||||
First create `vue-query.js` file in your `plugins` directory with the following content:
|
||||
|
||||
```js
|
||||
import Vue from 'vue'
|
||||
import { VueQueryPlugin, QueryClient, hydrate } from '@tanstack/vue-query'
|
||||
|
||||
export default (context) => {
|
||||
// Modify your Vue Query global settings here
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: { queries: { staleTime: 5000 } },
|
||||
})
|
||||
|
||||
if (process.server) {
|
||||
context.ssrContext.VueQuery = queryClient
|
||||
}
|
||||
|
||||
if (process.client) {
|
||||
Vue.use(VueQueryPlugin, { queryClient })
|
||||
|
||||
if (context.nuxtState && context.nuxtState.vueQueryState) {
|
||||
hydrate(queryClient, context.nuxtState.vueQueryState)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Add this plugin to your `nuxt.config.js`
|
||||
|
||||
```js
|
||||
module.exports = {
|
||||
...
|
||||
plugins: ['~/plugins/vue-query.js'],
|
||||
}
|
||||
```
|
||||
|
||||
Now you are ready to prefetch some data in your pages with `onServerPrefetch`.
|
||||
|
||||
- Use `useContext` to get nuxt context
|
||||
- Use `useQueryClient` to get server-side instance of `queryClient`
|
||||
- Prefetch all the queries that you need with `queryClient.query`, `queryClient.infiniteQuery`, or `suspense`
|
||||
- Dehydrate `queryClient` to the `nuxtContext`
|
||||
|
||||
```vue
|
||||
// pages/todos.vue
|
||||
<template>
|
||||
<div>
|
||||
<button @click="refetch">Refetch</button>
|
||||
<p>{{ data }}</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import {
|
||||
defineComponent,
|
||||
onServerPrefetch,
|
||||
useContext,
|
||||
} from '@nuxtjs/composition-api'
|
||||
import { useQuery, useQueryClient, dehydrate } from '@tanstack/vue-query'
|
||||
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
// Get QueryClient either from SSR context, or Vue context
|
||||
const { ssrContext } = useContext()
|
||||
// Make sure to provide `queryClient` as a second parameter to `useQuery` calls
|
||||
const queryClient =
|
||||
(ssrContext != null && ssrContext.VueQuery) || useQueryClient()
|
||||
|
||||
// This will be prefetched and sent from the server
|
||||
const { data, refetch, suspense } = useQuery(
|
||||
{
|
||||
queryKey: ['todos'],
|
||||
queryFn: getTodos,
|
||||
},
|
||||
queryClient,
|
||||
)
|
||||
// This won't be prefetched, it will start fetching on client side
|
||||
const { data2 } = useQuery(
|
||||
{
|
||||
queryKey: ['todos2'],
|
||||
queryFn: getTodos,
|
||||
},
|
||||
queryClient,
|
||||
)
|
||||
|
||||
onServerPrefetch(async () => {
|
||||
await suspense()
|
||||
ssrContext.nuxt.vueQueryState = dehydrate(queryClient)
|
||||
})
|
||||
|
||||
return {
|
||||
refetch,
|
||||
data,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
```
|
||||
|
||||
As demonstrated, it's fine to prefetch some queries and let others fetch on the client. This means you can control what content server renders or not by adding or removing `queryClient.query` or `suspense` for a specific query.
|
||||
|
||||
## Using Vite SSR
|
||||
|
||||
Sync VueQuery client state with [vite-ssr](https://github.com/frandiox/vite-ssr) in order to serialize it in the DOM:
|
||||
|
||||
```js
|
||||
// main.js (entry point)
|
||||
import App from './App.vue'
|
||||
import viteSSR from 'vite-ssr/vue'
|
||||
import {
|
||||
QueryClient,
|
||||
VueQueryPlugin,
|
||||
hydrate,
|
||||
dehydrate,
|
||||
} from '@tanstack/vue-query'
|
||||
|
||||
export default viteSSR(App, { routes: [] }, ({ app, initialState }) => {
|
||||
// -- This is Vite SSR main hook, which is called once per request
|
||||
|
||||
// Create a fresh VueQuery client
|
||||
const queryClient = new QueryClient()
|
||||
|
||||
// Sync initialState with the client state
|
||||
if (import.meta.env.SSR) {
|
||||
// Indicate how to access and serialize VueQuery state during SSR
|
||||
initialState.vueQueryState = { toJSON: () => dehydrate(queryClient) }
|
||||
} else {
|
||||
// Reuse the existing state in the browser
|
||||
hydrate(queryClient, initialState.vueQueryState)
|
||||
}
|
||||
|
||||
// Mount and provide the client to the app components
|
||||
app.use(VueQueryPlugin, { queryClient })
|
||||
})
|
||||
```
|
||||
|
||||
Then, call VueQuery from any component using Vue's `onServerPrefetch`:
|
||||
|
||||
```html
|
||||
<!-- MyComponent.vue -->
|
||||
<template>
|
||||
<div>
|
||||
<button @click="refetch">Refetch</button>
|
||||
<p>{{ data }}</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useQuery } from '@tanstack/vue-query'
|
||||
import { onServerPrefetch } from 'vue'
|
||||
|
||||
// This will be prefetched and sent from the server
|
||||
const { refetch, data, suspense } = useQuery({
|
||||
queryKey: ['todos'],
|
||||
queryFn: getTodos,
|
||||
})
|
||||
|
||||
onServerPrefetch(suspense)
|
||||
</script>
|
||||
```
|
||||
|
||||
## Tips, Tricks and Caveats
|
||||
|
||||
### Only successful queries are included in dehydration
|
||||
|
||||
Any query with an error is automatically excluded from dehydration. This means that the default behavior is to pretend these queries were never loaded on the server, usually showing a loading state instead, and retrying the queries on the queryClient. This happens regardless of error.
|
||||
|
||||
Sometimes this behavior is not desirable, maybe you want to render an error page with a correct status code instead on certain errors or queries. In those cases, use `queryClient.query` and catch any errors to handle those manually.
|
||||
|
||||
### Staleness is measured from when the query was fetched on the server
|
||||
|
||||
A query is considered stale depending on when it was `dataUpdatedAt`. A caveat here is that the server needs to have the correct time for this to work properly, but UTC time is used, so timezones do not factor into this.
|
||||
|
||||
Because `staleTime` defaults to `0`, queries will be refetched in the background on page load by default. You might want to use a higher `staleTime` to avoid this double fetching, especially if you don't cache your markup.
|
||||
|
||||
This refetching of stale queries is a perfect match when caching markup in a CDN! You can set the cache time of the page itself decently high to avoid having to re-render pages on the server, but configure the `staleTime` of the queries lower to make sure data is refetched in the background as soon as a user visits the page. Maybe you want to cache the pages for a week, but refetch the data automatically on page load if it's older than a day?
|
||||
|
||||
### High memory consumption on server
|
||||
|
||||
In case you are creating the `QueryClient` for every request, Vue Query creates the isolated cache for this client, which is preserved in memory for the `gcTime` period. That may lead to high memory consumption on server in case of high number of requests during that period.
|
||||
|
||||
On the server, `gcTime` defaults to `Infinity` which disables manual garbage collection and will automatically clear memory once a request has finished. If you are explicitly setting a non-Infinity `gcTime` then you will be responsible for clearing the cache early.
|
||||
|
||||
To clear the cache after it is not needed and to lower memory consumption, you can add a call to [`queryClient.clear()`](../reference/classes/QueryClient.md#clear) after the request is handled and dehydrated state has been sent to the client.
|
||||
|
||||
## `dehydrate`/`hydrate` options
|
||||
|
||||
`dehydrate` and `hydrate` are re-exported from `@tanstack/query-core`, so they're available directly from `@tanstack/vue-query` — as used throughout this guide.
|
||||
|
||||
`dehydrate(client, options?)` accepts a `DehydrateOptions` object:
|
||||
|
||||
- `shouldDehydrateMutation: (mutation) => boolean` — whether to dehydrate a given mutation. Called for each mutation in the cache; defaults to only including paused mutations. To extend the default behavior instead of replacing it, import and call `defaultShouldDehydrateMutation` as part of your return statement.
|
||||
- `shouldDehydrateQuery: (query) => boolean` — whether to dehydrate a given query. Called for each query in the cache; defaults to only including successful queries (see [above](#only-successful-queries-are-included-in-dehydration)). Extend it the same way, with `defaultShouldDehydrateQuery`.
|
||||
- `serializeData?: (data) => any` — transforms (serializes) data during dehydration.
|
||||
- `shouldRedactErrors?: (error) => boolean` — only applies to queries still `pending` at dehydration time, whose promise is dehydrated too: decides whether to redact the error if that promise later rejects. Defaults to redacting all such errors. Does **not** apply to `query.state.error` on already-settled queries — that error is included in the dehydrated state as-is, so sanitize it yourself via `shouldDehydrateQuery` if it may contain sensitive data.
|
||||
|
||||
`hydrate(client, dehydratedState, options?)` accepts a `HydrateOptions` object:
|
||||
|
||||
- `defaultOptions.mutations` — default mutation options for the hydrated mutations.
|
||||
- `defaultOptions.queries` — default query options for the hydrated queries.
|
||||
- `defaultOptions.deserializeData?: (data) => any` — transforms (deserializes) data before it's put into the cache.
|
||||
|
||||
One caveat with `hydrate`: if a query being hydrated already exists in the cache, it's only overwritten when the incoming data is newer than what's already there — otherwise it's silently skipped.
|
||||
|
||||
Alternatively, you can set a smaller `gcTime`.
|
||||
57
docs/framework/vue/guides/suspense.md
Normal file
57
docs/framework/vue/guides/suspense.md
Normal file
@@ -0,0 +1,57 @@
|
||||
---
|
||||
id: suspense
|
||||
title: Suspense (experimental)
|
||||
---
|
||||
|
||||
> NOTE: Suspense mode for Vue Query is experimental, same as Vue's Suspense itself. These APIs WILL change and should not be used in production unless you lock both your Vue and Vue Query versions to patch-level versions that are compatible with each other.
|
||||
|
||||
Vue Query can also be used with Vue's new [Suspense](https://vuejs.org/guide/built-ins/suspense.html) API's.
|
||||
|
||||
To do that you need to wrap your suspendable component with `Suspense` component provided by Vue
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
import SuspendableComponent from './SuspendableComponent.vue'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Suspense>
|
||||
<template #default>
|
||||
<SuspendableComponent />
|
||||
</template>
|
||||
<template #fallback>
|
||||
<div>Loading...</div>
|
||||
</template>
|
||||
</Suspense>
|
||||
</template>
|
||||
```
|
||||
|
||||
And change your `setup` function in suspendable component to be `async`. Then you can use async `suspense` function that is provided by `vue-query`.
|
||||
|
||||
```vue
|
||||
<script>
|
||||
import { defineComponent } from 'vue'
|
||||
import { useQuery } from '@tanstack/vue-query'
|
||||
|
||||
const todoFetcher = async () =>
|
||||
await fetch('https://jsonplaceholder.cypress.io/todos').then((response) =>
|
||||
response.json(),
|
||||
)
|
||||
export default defineComponent({
|
||||
name: 'SuspendableComponent',
|
||||
async setup() {
|
||||
const { data, suspense } = useQuery({
|
||||
queryKey: ['todos'],
|
||||
queryFn: todoFetcher,
|
||||
})
|
||||
await suspense()
|
||||
|
||||
return { data }
|
||||
},
|
||||
})
|
||||
</script>
|
||||
```
|
||||
|
||||
## Fetch-on-render vs Render-as-you-fetch
|
||||
|
||||
Out of the box, Vue Query in `suspense` mode works really well as a **Fetch-on-render** solution with no additional configuration. This means that when your components attempt to mount, they will trigger query fetching and suspend, but only once you have imported them and mounted them. If you want to take it to the next level and implement a **Render-as-you-fetch** model, we recommend implementing [Prefetching](./prefetching) on routing callbacks and/or user interactions events to start loading queries before they are mounted and hopefully even before you start importing or mounting their parent components.
|
||||
4
docs/framework/vue/guides/testing.md
Normal file
4
docs/framework/vue/guides/testing.md
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
id: testing
|
||||
title: Testing
|
||||
---
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
id: updates-from-mutation-responses
|
||||
title: Updates from Mutation Responses
|
||||
ref: docs/framework/react/guides/updates-from-mutation-responses.md
|
||||
---
|
||||
25
docs/framework/vue/guides/window-focus-refetching.md
Normal file
25
docs/framework/vue/guides/window-focus-refetching.md
Normal file
@@ -0,0 +1,25 @@
|
||||
---
|
||||
id: window-focus-refetching
|
||||
title: Window Focus Refetching
|
||||
ref: docs/framework/react/guides/window-focus-refetching.md
|
||||
replace: { '@tanstack/react-query': '@tanstack/vue-query' }
|
||||
---
|
||||
|
||||
[//]: # 'Example'
|
||||
|
||||
```js
|
||||
const vueQueryPluginOptions: VueQueryPluginOptions = {
|
||||
queryClientConfig: {
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
refetchOnWindowFocus: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
app.use(VueQueryPlugin, vueQueryPluginOptions)
|
||||
```
|
||||
|
||||
[//]: # 'Example'
|
||||
[//]: # 'ReactNative'
|
||||
[//]: # 'ReactNative'
|
||||
Reference in New Issue
Block a user