Skip to content

@tanstack/vue-query: Generated functions lack Vue 3 reactivity supportΒ #2719

@adamkasper

Description

@adamkasper

Description

The @tanstack/vue-query plugin generates query option functions that don't support Vue 3's reactivity system. Generated functions accept static Options parameters instead of reactive values (MaybeRefOrGetter), which prevents queries from automatically refetching when reactive dependencies change.

Current problematic behavior:

const userId = ref('user123')
const searchQuery = ref('john')

const { data } = useQuery({
  ...getUsersOptions({
    path: { id: userId.value }, // ❌ .value loses reactivity
    query: { search: searchQuery.value }, // ❌ .value loses reactivity
  }),
})

// These changes don't trigger refetch:
userId.value = 'user456' // No refetch
searchQuery.value = 'jane' // No refetch

Expected behavior:

const userId = ref('user123')
const searchQuery = ref('john')

const { data } = useQuery({
  ...getUsersOptions({
    path: { id: userId }, // βœ… Pass ref directly
    query: { search: searchQuery }, // βœ… Pass ref directly
  }),
})

// These should trigger automatic refetch:
userId.value = 'user456' // βœ… Refetch
searchQuery.value = 'jane' // βœ… Refetch

Root cause:

The plugin currently generates:

export function getUsersOptions(options: Options<GetUsersData>) {
  return queryOptions({
    queryFn: async ({ queryKey, signal }) => { /* ... */ },
    queryKey: getUsersQueryKey(options), // Static queryKey!
  })
}

It should generate:

export function getUsersOptions(options: MaybeRefOrGetter<Options<GetUsersData>>) {
  return queryOptions({
    queryFn: async ({ queryKey, signal }) => {
      const resolvedOptions = toValue(options) // Unwrap reactive values
      // ...
    },
    queryKey: computed(() => getUsersQueryKey(options)), // Reactive queryKey!
  })
}

Proposed solution:

Create Vue-specific implementations in @tanstack/vue-query plugin:

  1. Import Vue utilities: MaybeRefOrGetter, toValue, computed from 'vue'
  2. Wrap function parameters with MaybeRefOrGetter<Options<T>>
  3. Use computed(() => queryKey(...)) for reactive query keys
  4. Use toValue(options) inside queryFn to resolve reactive values

This approach aligns with TanStack Vue Query's design principles and is similar to how @pinia/colada handles reactive parameters.

Reproducible example or configuration

https://stackblitz.com/edit/nuxt-starter-ctyy1wrx?file=app%2Fpages%2Findex.vue

OpenAPI specification (optional)

No response

System information (optional)

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions