Last active
June 8, 2025 02:44
-
-
Save wobsoriano/9a7de2d2aaf9448c2fb952d2746b6907 to your computer and use it in GitHub Desktop.
TanStack Query + Vue Options API
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
<script lang="ts"> | |
import { defineComponent, toRaw } from 'vue' | |
import { | |
QueryObserver, | |
type QueryKey, | |
type QueryObserverResult, | |
type QueryClient, | |
} from '@tanstack/query-core' | |
type Todo = { | |
userId: number | |
id: number | |
title: string | |
completed: boolean | |
} | |
export default defineComponent({ | |
inject: ['queryClient'], | |
data: () => ({ | |
todoId: 1, | |
result: {} as QueryObserverResult<Todo, unknown>, | |
observer: null as null | QueryObserver<Todo, unknown, Todo, Todo, QueryKey>, | |
unsubscribe: () => {} | |
}), | |
methods: { | |
async fetchTodo(id: number) { | |
const resp = await fetch('https://jsonplaceholder.typicode.com/todos/' + id) | |
const data = await resp.json() | |
return data | |
}, | |
}, | |
mounted() { | |
this.observer = new QueryObserver<Todo, unknown, Todo, Todo, QueryKey>(this.queryClient as QueryClient, { | |
queryKey: ['todo', 1], | |
queryFn: () => this.fetchTodo(1), | |
}) | |
this.unsubscribe = this.observer.subscribe((result) => { | |
Object.keys(result).forEach((key) => { | |
// @ts-expect-error: Incompatible types | |
this.result[key] = result[key] | |
}) | |
}) | |
}, | |
beforeUnmount() { | |
this.unsubscribe() | |
}, | |
watch: { | |
todoId(id) { | |
toRaw(this.observer)?.setOptions({ | |
queryKey: ['todo', id], | |
queryFn: () => this.fetchTodo(id), | |
}) | |
} | |
} | |
}) | |
</script> | |
<template> | |
<main> | |
<div v-if="result.isLoading">Loading...</div> | |
<div v-else>{{ JSON.stringify(result.data) }}</div> | |
<button @click="todoId++" :disabled="result.isLoading"> | |
{{ result.isLoading ? 'Fetching...' : 'Next todo' }} | |
</button> | |
</main> | |
</template> |
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 { createApp } from 'vue' | |
import App from './App.vue' | |
import { QueryClient } from '@tanstack/query-core' | |
const app = createApp(App) | |
const queryClient = new QueryClient() | |
app.provide('queryClient', queryClient) | |
app.mount('#app') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@Tajcore It is very late, but I found out that tanstack/vue-query integrates just fine in Options API. I am using
[email protected]
.Follow the installation instruction. Add these to the entry point.
Note that my setup is a little unconventional because my index.html entry point is like this. For context, my tech stack is Laravel + Vue. The important part is
UserApp
component.In
UserApp
, provide thequeryClient
. I tried to useuseQueryClient
directly indata
, but it throws an error. Something about the hook needs to be called in asetup()
environment or has a provide context.If the project uses mixins, placing the
inject
in the mixins configuration might provide some shortcuts in further development.Alternatively, you can just add a single
setup()
function in Options API. However, you need to do this in every component that needs access to query client.Now, for
useQuery
. It is possible to just use it directly indata
. It will still be reactive, though with some caviat. A reactive queryKey must be wrapped inreactive
orref
.Any changes to filter will be reactive. The query will
refetch
every time filter is modified.Mutations are similar.
I created an example CodeSandbox . Currently, I am adding another example inspired by TkDodo.