Skip to content

Vue - Query key doesn't match the current value of ref used inside #5910

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
markbrockhoff opened this issue Aug 25, 2023 · 7 comments · Fixed by #5929
Closed

Vue - Query key doesn't match the current value of ref used inside #5910

markbrockhoff opened this issue Aug 25, 2023 · 7 comments · Fixed by #5929

Comments

@markbrockhoff
Copy link
Contributor

Describe the bug

Hi,

I was trying to build a filter for some data using tanstack query.
It's supposed to only refetch the data with the given values whenever the user clicks a button so not automatically.
Therfore I set enabled to false to only execute the query when manually calling refetch().

When testing it I encountered some wired behavior: The query key seems to always lack behind one iteration from the actual value of my filter.
I created a minimal example of the issue to make it reproducable and easier to explain:

<script setup lang="ts">
import { useQuery } from '@tanstack/vue-query';
import { nextTick, onMounted, ref } from 'vue';

const id = ref(0);

const {data, refetch} = useQuery({
  queryKey: ['multiply', id] as const,
  queryFn: async ({queryKey}) => {
    console.log("Current value of ref:", id.value);
    console.log("Current value of query key:", queryKey[1]); // Will always be one step behind
    
    return Promise.resolve(id.value * 2)
  },
  enabled: false
});
onMounted(() => refetch());

async function increment() {
  console.log('Increment clicked');
  id.value++;
  // await nextTick(); // Awaiting the next tick before refetching fixes the issue
  refetch();
}
</script>

<template>
  <div>Id times two: {{ data }}</div>
  <button @click="increment">Increment id</button>
</template>

In the above example I want to update data with the new value of id multiplied by two whenever the Increment button is clicked. (This could of course be done without manually calling refetch() but I needed a simple example)

Whenever the increment button is clicked the id gets incremented correctly but the query used during the refetch will still use the old value of id. Therfore the result is assigned to the old key:

image

Meanwhile data is always undefined as the key changes right after the data was fetched.
If I manually await the next tick inside vues' reactivity system the value is updated correctly before the refetch is triggered and everything works as expected.
That's why I guess it could be something related to the reactivity chain of the queryKey. Maybe a lazily evaluated computed or something similar.

Your minimal, reproducible example

https://github.com/markbrockhoff/tanstack-query-bug-repro

Steps to reproduce

  1. Clone the reproduction repo
  2. Install the dependencies: pnpm i
  3. Start the project: pnpm dev
  4. Click the "Increment" button and look at the logs inside the console

Expected behavior

I'd expect the changes to reactive parts of the queryKey to take affect immediately so the refetch uses the correct queryKey without manually awaiting the nextTick before triggering it.

How often does this bug happen?

Every time

Screenshots or Videos

No response

Platform

  • OS: macos
  • Browser: Chrome

Tanstack Query adapter

vue-query

TanStack Query version

v4.34.0

TypeScript version

5.2.2

Additional context

No response

@DamianOsipiuk
Copy link
Contributor

Yes, you are correct.
We can fix it by using flush: 'sync' on options watcher.

Do you want to provide a fix for it?
It would be nice to make this change in all of our composables.

@Mini-ghost
Copy link
Contributor

Mini-ghost commented Aug 26, 2023

@markbrockhoff

Additionally, the following code may resolve your issue:

<script setup lang="ts">
import { useQuery } from '@tanstack/vue-query';
import { onMounted, ref } from 'vue';

const id = ref(0);
const isMounted = ref(false);

const { data } = useQuery({
  queryKey: ['multiply', id] as const,
  queryFn: async ({queryKey}) => {
    console.log("Current value of ref:", id.value);
    console.log("Current value of query key:", queryKey[1]);
    
    return Promise.resolve(id.value * 2)
  },
  enabled: isMounted
});

onMounted(() => isMounted.value = true);

async function increment() {
  console.log('Increment clicked');
  id.value++;
}
</script>

@markbrockhoff
Copy link
Contributor Author

Hi @DamianOsipiuk, I tried and got as far as fixing my issue. But unfortunately it looks like adding flush sync to the watchers breaks dependent queries when providing a computed to the enabled property.
My current guess is that enabled is still undefined at the time the query options are evaluated. If you wan't feel free to take a look at the current progress (https://github.com/markbrockhoff/tanstack-query/tree/5910-fix-vue-query-key-sync-delay) but I'm not sure how this could be handled as the enabled property would need to be handled differently from the query key...

@markbrockhoff
Copy link
Contributor Author

@markbrockhoff

Additionally, the following code may resolve your issue:

<script setup lang="ts">
import { useQuery } from '@tanstack/vue-query';
import { onMounted, ref } from 'vue';

const id = ref(0);
const isMounted = ref(false);

const { data } = useQuery({
  queryKey: ['multiply', id] as const,
  queryFn: async ({queryKey}) => {
    console.log("Current value of ref:", id.value);
    console.log("Current value of query key:", queryKey[1]);
    
    return Promise.resolve(id.value * 2)
  },
  enabled: isMounted
});

onMounted(() => isMounted.value = true);

async function increment() {
  console.log('Increment clicked');
  id.value++;
}
</script>

Thanks, it does solve the issue but also causes the queryFn to be triggered twice which is not exactly what I want. :)

@Mini-ghost
Copy link
Contributor

@markbrockhoff

Looking forward to your PR.

Additionally, queryFn doesn't seem to have been triggered twice. Under what circumstances would the issue you referred to occur?

2023-08-28.7.10.04.mov

@markbrockhoff
Copy link
Contributor Author

@Mini-ghost you're right, my bad. I probably still had the refetch in there. But without it, the code won't longer have the desired behavior as it would now be triggered by a change of the key. But my goal is to only run the query when manually calling refetch.

@Mini-ghost
Copy link
Contributor

I looked at the content you forked from this repository; perhaps it could solve the problem. Additionally until the issue is resolved, the above-mentioned method maybe can be considered as a temporary alternative solution.

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

Successfully merging a pull request may close this issue.

3 participants