N
Nuxt3mo ago
tommywayss

Watch/Dedupe issue

Minimal example
const { date } = useDateRange() // date: Ref<{ start: Date; end: Date }>
const { data, pending, error } = await useFetch<T>('/api/any', {
query: {
started_after: computed(() => date.value.start.toISOString()),
finished_before: computed(() => date.value.end.toISOString()),
},
})

// periodically reassign the whole object (simulates a single user action)
useTimeoutPoll(() => {
date.value = { start: new Date(), end: new Date() }
}, 1000)
const { date } = useDateRange() // date: Ref<{ start: Date; end: Date }>
const { data, pending, error } = await useFetch<T>('/api/any', {
query: {
started_after: computed(() => date.value.start.toISOString()),
finished_before: computed(() => date.value.end.toISOString()),
},
})

// periodically reassign the whole object (simulates a single user action)
useTimeoutPoll(() => {
date.value = { start: new Date(), end: new Date() }
}, 1000)
Actual behavior
/api/any 200 // initial
/api/any 200 // first trigger
/api/any Cancelled // also first trigger but deduped with cancellation
/api/any 200 // second trigger
/api/any 200 // third trigger
/api/any 200 // initial
/api/any 200 // first trigger
/api/any Cancelled // also first trigger but deduped with cancellation
/api/any 200 // second trigger
/api/any 200 // third trigger
Expected behavior
/api/any 200 // initial
/api/any 200 // first trigger
/api/any 200 // second trigger
/api/any 200 // third trigger
/api/any 200 // initial
/api/any 200 // first trigger
/api/any 200 // second trigger
/api/any 200 // third trigger
Observation When a key parameter is added to useFetch, the behavior is correct. Without a key, the duplicated request and cancellation occurs.
5 Replies
kapa.ai
kapa.ai3mo ago
Hi @tommywayss. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- Your observation is correct and aligns with how Nuxt's data fetching system works. When you use reactive query parameters in useFetch without specifying a custom key, Nuxt auto-generates a key based on the URL and options. However, rapid changes to reactive sources (like reassigning the whole date object) can cause multiple fetches to be triggered in quick succession, leading to deduplication and cancellation behavior as you described. Why does this happen? - By default, useFetch deduplicates requests using the dedupe: 'cancel' strategy, which cancels any pending request if a new one is triggered with the same key. - When you don't provide a custom key, the auto-generated key may not always reflect the full uniqueness of your query parameters, especially if they are rapidly changing computed values. This can result in Nuxt treating two rapid requests as "the same," cancelling the first in favor of the second. Why does adding a key fix it? - Providing a custom key (for example, one that incorporates the current date range) ensures that each fetch is treated as a unique request, so deduplication does not cancel the previous request unless the key is exactly the same. This matches your expected behavior. Relevant documentation: - Nuxt uses keys to prevent refetching the same data. If you don't provide a key, it is generated from the URL and options, but for complex or rapidly changing queries, a custom key is recommended for correct deduplication behavior. See the Nuxt docs on useFetch parameters and keys. - The dedupe option defaults to 'cancel', which cancels existing requests when a new one is made with the same key. See dedupe option. - If you need independent instances or want to avoid deduplication issues, use different keys:
// These are completely independent instances
const { data: users1 } = useAsyncData('users-1', () => $fetch('/api/users'))
const { data: users2 } = useAsyncData('users-2', () => $fetch('/api/users'))

// These are completely independent instances
const { data: users1 } = useAsyncData('users-1', () => $fetch('/api/users'))
const { data: users2 } = useAsyncData('users-2', () => $fetch('/api/users'))

Shared State and Option Consistency Summary:
To avoid unexpected deduplication and cancellation, always provide a unique key to useFetch when your query parameters are reactive and can change rapidly. This ensures each request is treated independently and matches your expected behavior. If you need more control, you can also adjust the dedupe option, but providing a unique key is the most robust solution. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai3mo ago
<@551087428077748255> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
tommywayss
tommywayssOP3mo ago
Additional expectation useFetch already generates a default key when none is provided. The expectation is that the default key generated by useFetch should behave the same as providing an explicit key option — i.e. ensure one request per reactive change within a component instance. If key like key: computed(() => store.api.any.${hash(date.value)}) again first trigger dedupes and brakes data @kapa.ai Why only first watch trigger occurs double request?
kapa.ai
kapa.ai3mo ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- The double request on the first watch trigger is due to how Nuxt's useFetch deduplication and reactivity system handle rapid changes in reactive parameters, especially when no explicit key is provided. When you reassign the whole object (like your date range), multiple reactive dependencies (e.g., started_after and finished_before) update almost simultaneously. This can cause useFetch to trigger multiple fetches in quick succession. With the default dedupe: 'cancel' strategy, the first request is started, but as soon as the second dependency updates, a new request is triggered and the previous one is cancelled. This results in two requests for the first change: one that is cancelled and one that completes. On subsequent changes, the system is already "in sync," so only one request is triggered per change. This behavior is documented in a related GitHub issue, where it was observed that the watcher fires too early—before all reactive properties are updated—leading to requests with incomplete or out-of-sync parameters. The deduplication then cancels the first request in favor of the second, causing the double request only on the first trigger after a batch update to the reactive object. This is a side effect of how Vue's synchronous watchers and Nuxt's deduplication interact when multiple dependencies change at once without batching or a custom key to distinguish requests GitHub Issue: Watched params breaks watcher/js expected synchronicity. Adding a custom key or batching updates can prevent this, as you observed. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai3mo ago
<@551087428077748255> Kapa.ai is still learning and improving, please let me know how I did by reacting below.

Did you find this page helpful?