Skip to content

Instantly share code, notes, and snippets.

@Sleavely
Last active December 12, 2022 18:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Sleavely/d4e3335a900a71213c0f754239a998d6 to your computer and use it in GitHub Desktop.
Save Sleavely/d4e3335a900a71213c0f754239a998d6 to your computer and use it in GitHub Desktop.
@tanstack/query-core, with Vue's Options API

Ever wanted to benefit from all the cool features of @tanstack/vue-query but your project doesn't use Composition API? Me too. The files below illustrate a crude - but working - implementation that abuses the reactivity of data in Vue components.

I've successfully used this approach in a Nuxt app where multiple components needed to some data from an API but I didn't want to implement my own caching mechanism in Vuex for keeping track of which IDs are already present or being fetched.

I'm sure it doesn't cover every conceivable usecase, but it did the trick for me.

import { QueryClient, QueryObserver } from '@tanstack/query-core'
export const queryClient = new QueryClient({
defaultOptions: {
queries: {
// Cache data by default for 5 minutes
staleTime: 5 * 60 * 1000,
},
},
})
export const getQuery = (query) => {
const queryObserver = new QueryObserver(queryClient, query)
const proxyObject = { ...queryObserver.getCurrentResult() }
// Force the query out of idle mode by subscribing to it
queryObserver.subscribe((newResult) => {
// Update each prop on the query with new values.
// Since we're modifying an object Vue already observes in our components `data`
// it will automatically be reflected in our UI
for (const [key, val] of Object.entries(newResult)) {
proxyObject[key] = val
}
})
return proxyObject
}
<template>
<div v-if="user.data">
{{ user.data.name }}
</div>
</template>
<script>
import getQuery from './queryManager'
export default {
props: {
userId: {
type: String,
default: '',
},
},
data: {
user: getQuery({
queryKey: ['user', this.userId],
queryFn: () => this.$axios.$get(`${this.$config.usersApi}/${this.userId}`),
placeholderData: {},
}),
},
}
</script>
@Sleavely
Copy link
Author

Sleavely commented Dec 12, 2022

A more realistic Vue template that handles the loading and error states might look something like:

<template>
  <section class="users">

    <LoadingSpinner v-if="users.isLoading">
      Loading users..
    </LoadingSpinner>

    <ErrorMessage v-if="users.isError">
      {{ users.error }}
    </ErrorMessage>

    <!-- No data -->
    <p v-if="users.isSuccess && !users.data.length">
      <em>Acme Inc</em> has no users.
    </p>

    <div v-if="users.isSuccess && users.data" class="users-grid">
      <UserProfileCard v-for="user in users.data" :key="user.id">
        <img class="user-avatar" :src="user.avatarUrl" />
        <span class="user-name">{{ user.name }}</span>
      </UserProfileCard>
    </div>

  </section>
</template>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment