Skip to content

Instantly share code, notes, and snippets.

@nawie
Created April 18, 2024 04:28
Show Gist options
  • Save nawie/e70ad50361fc84e9c39f5bf51ed34660 to your computer and use it in GitHub Desktop.
Save nawie/e70ad50361fc84e9c39f5bf51ed34660 to your computer and use it in GitHub Desktop.
Composable Tanstack Query for Vue 3
import { z } from 'zod'
import { ref } from 'vue'
import type { DefaultError, QueryKey } from "@tanstack/query-core";
import type { QueryClient } from "@tanstack/vue-query/src/queryClient";
import {
UseQueryDefinedReturnType,
UseQueryOptions,
UseQueryReturnType,
useQuery as useVueQuery,
} from "@tanstack/vue-query";
import type {
UndefinedInitialQueryOptions,
DefinedInitialQueryOptions,
} from "@tanstack/vue-query/src/useQuery";
/**
* query network mode
* docs: https://tanstack.com/query/latest/docs/framework/react/guides/network-mode#network-mode
* - online:
* In this mode, Queries and Mutations will not fire unless you have network connection. This is the default mode.
* If a fetch is initiated for a query, it will always stay in the state (pending, error, success) it is in if the fetch cannot be made because there is no network connection. However, a fetchStatus is exposed additionally. This can be either:
* - fetching: The queryFn is really executing - a request is in-flight.
* - paused: The query is not executing - it is paused until you have connection again
* - idle: The query is not fetching and not paused
*
* - always:
* In this mode, TanStack Query will always fetch and ignore the online / offline state.
* This is likely the mode you want to choose if you use TanStack Query in an environment where you don't
* need an active network connection for your Queries to work - e.g. if you just read from AsyncStorage,
* or if you just want to return Promise.resolve(5) from your queryFn
*
* - offlineFirst:
* This mode is the middle ground between the first two options, where TanStack Query will run the queryFn
* once, but then pause retries. This is very handy if you have a serviceWorker that intercepts a
* request for caching like in an offline-first PWA, or if you use HTTP caching via the Cache-Control header.
*/
const networkMode = z.enum(['online', 'always', 'offlineFirst'])
type NetworkMode = z.infer<typeof networkMode>
// set always as default of useQuery options
const globalNetworkMode = ref<NetworkMode>(networkMode.enum.always)
export function setDefaultNetworkMode(mode: NetworkMode): void {
globalNetworkMode.value = networkMode.parse(mode)
}
export function getDefaultNetworkMode(): NetworkMode {
return globalNetworkMode.value
}
export function useQuery<
TQueryFnData = unknown,
TError = DefaultError,
TData = TQueryFnData,
TQueryKey extends QueryKey = QueryKey,
>(
options: UndefinedInitialQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
queryClient?: QueryClient,
): UseQueryReturnType<TData, TError>;
export function useQuery<
TQueryFnData = unknown,
TError = DefaultError,
TData = TQueryFnData,
TQueryKey extends QueryKey = QueryKey,
>(
options: DefinedInitialQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
queryClient?: QueryClient,
): UseQueryDefinedReturnType<TData, TError>;
export function useQuery<
TQueryFnData = unknown,
TError = DefaultError,
TData = TQueryFnData,
TQueryKey extends QueryKey = QueryKey,
>(
options: UseQueryOptions<
TQueryFnData,
TError,
TData,
TQueryFnData,
TQueryKey
>,
queryClient?: QueryClient,
): UseQueryReturnType<TData, TError>;
export function useQuery<
TQueryFnData,
TError = DefaultError,
TData = TQueryFnData,
TQueryKey extends QueryKey = QueryKey,
>(
options: UseQueryOptions<
TQueryFnData,
TError,
TData,
TQueryFnData,
TQueryKey
>,
queryClient?: QueryClient,
ignoreNetworkMode: boolean = false // when true, is same as set default networkMode to always
):
| UseQueryReturnType<TData, TError>
| UseQueryDefinedReturnType<TData, TError> {
/**
* set default network mode when options has no networkMode
*/
const defaultNetworkMode = ignoreNetworkMode ? networkMode.enum.always : getDefaultNetworkMode()
const mergeOptions = {
...options,
networkMode: 'networkMode' in options ? options.networkMode : defaultNetworkMode,
} as UseQueryOptions<
TQueryFnData,
TError,
TData,
TQueryFnData,
TQueryKey
>
return useVueQuery(mergeOptions, queryClient);
}
export default useQuery
@nawie
Copy link
Author

nawie commented Apr 18, 2024

Composable Tanstack query for Vue 3

Used to override default useQuery options from tanstack query, by default it set default network mode to always

  1. Install tanstack query and zod
npm i @tanstack/vue-query zod
  1. Configure tanstack query on vue main.ts
import { createApp } from 'vue'
import { VueQueryPlugin } from '@tanstack/vue-query'
import App from './App.vue'

import { VueQueryPlugin } from '@tanstack/vue-query'

const app = createApp(App)
  .use(VueQueryPlugin);

app.mount('#app')
  1. Important copy useQuery.ts into composables directory
git clone https://gist.github.com/e70ad50361fc84e9c39f5bf51ed34660.git ./src/composables/
  1. Optional modify / customize useQuery.ts default options
export function useQuery<
  TQueryFnData,
  TError = DefaultError,
  TData = TQueryFnData,
  TQueryKey extends QueryKey = QueryKey,
>(
  options: UseQueryOptions<
    TQueryFnData,
    TError,
    TData,
    TQueryFnData,
    TQueryKey
  >,
  queryClient?: QueryClient,
  ignoreNetworkMode: boolean = false // when true, is same as set default networkMode to always
):
  | UseQueryReturnType<TData, TError>
  | UseQueryDefinedReturnType<TData, TError> {

  /**
   * set default network mode when options has no networkMode
   */
  const defaultNetworkMode = ignoreNetworkMode ? networkMode.enum.always : getDefaultNetworkMode()

  const mergeOptions = {
    ...options,
    networkMode: 'networkMode' in options ? options.networkMode : defaultNetworkMode,
    retry: 2, // >> [additional default options] Will retry failed requests n times before displaying an error,
  } as UseQueryOptions<
    TQueryFnData,
    TError,
    TData,
    TQueryFnData,
    TQueryKey
  >
  return useVueQuery(mergeOptions, queryClient);
}
  1. Use on components or another composables
<script setup lang="ts">
// component/onlineStatusLabel.vue
import { useQuery } from '@/composables/useQuery'

const { data, isFetching } = useQuery({
  queryKey: ['server', 'status'],
  queryFn: async () => await fetch(...), // fetch server status
  retry: 1, // Will retry failed requests n times before displaying an error
  refetchInterval: 2 * (1000 * 60), // ms
})
</script>

<template>
  <div>
    server status: {{ data?.status || 'offline' }}
  </div>
</template>

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