-
-
Save wobsoriano/9a7de2d2aaf9448c2fb952d2746b6907 to your computer and use it in GitHub Desktop.
| <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> |
| 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') |
Hey @wobsoriano, how would you handle mutations? with just query-core
@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.
app.createApp({});
import UserApp from './user/UserApp.vue';
app.component('UserApp', UserApp);
// ...
+ import { VueQueryPlugin } from '@tanstack/vue-query'
+ app.use(VueQueryPlugin)
app.mount('#app');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.
<div id="app">
<user-app :constant="{{ json_encode(array_merge($constant)) }}" />
</div>In UserApp, provide the queryClient. I tried to use useQueryClient directly in data, but it throws an error. Something about the hook needs to be called in a setup() environment or has a provide context.
<script>
import { useQueryClient } from '@tanstack/vue-query';
export default {
provide(){
return {
queryClient: useQueryClient(),
}
},
// ...
}
</script>
<template>
<!-- Content -->
</template>If the project uses mixins, placing the inject in the mixins configuration might provide some shortcuts in further development.
export default {
inject: ['queryClient'],
// ...
}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.
<script>
// Alternative method, implicitly use useQueryClient only when needed.
import { useQueryClient } from '@tanstack/vue-query';
export default {
setup(){
return {
queryClient: useQueryClient(),
};
}
// ...
}
</script>
<template>
<!-- Content -->
</template>Now, for useQuery. It is possible to just use it directly in data. It will still be reactive, though with some caviat. A reactive queryKey must be wrapped in reactive or ref.
<script>
// Alternative method, implicitly use useQueryClient only when needed.
import { useQueryClient } from '@tanstack/vue-query';
export default {
data(){
const filter = reactive({
search: '',
});
return {
filter,
query: useQuery({
queryKey: ['items', filter]
}),
};
}
}
</script>
<template>
<!-- Content -->
</template>Any changes to filter will be reactive. The query will refetch every time filter is modified.
Mutations are similar.
<script>
// Alternative method, implicitly use useQueryClient only when needed.
import { useQueryClient } from '@tanstack/vue-query';
export default {
data(){
const filter = reactive({
search: '',
});
return {
filter,
query: useQuery({
queryKey: ['items', filter],
queryFn: () => { /* ... */ },
}),
mutate: useMutation({
mutationFn: this.submit,
onSuccess: this.success,
}),
};
},
methods: {
submit(){
// ...
},
success(){
// ...
},
}
}
</script>
<template>
<!-- Content -->
</template>I created an example CodeSandbox . Currently, I am adding another example inspired by TkDodo.
Oh word I'm actually writing this using a class based approach using
vue-facing-decoratorthat doesn't support the dynamic nature of updating the queryKey sadly. would you update the gist though just asking?Thanks seeing it now!