useFetch not updating data ref when new request is triggered.
This is my simplified version of composable
I have the following issue. I call the composable in two components and pass the same id (ref) as arg
const { data } = await useProject(projectId)
when the projectId changes I see that useFetch will fire two requests however watch(data, (updated) => {
is called only in one instance. Why that could be?10 Replies
Hi @hackal. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:
----------------------
The issue you're experiencing is related to how
useFetch
handles caching and key generation. When you use useFetch
in multiple components with the same URL and options, Nuxt treats them as the same request and shares the data between them.
The Problem
The main issue in your code is that useFetch
is being called in multiple components with the same parameters, causing them to share state. When the second component calls the same useFetch
, it's using the same internal key, so only one of the watchers gets triggered when data changes.
This is a common issue when wrapping useFetch
in a custom composable. As mentioned in a Stack Overflow answer:
Generally as a rule guiding composables, they must be called only in; a setup function, Plugins, & Route Middleware. Trying to use this composable outside this scope will lead to bugs that affects performance of the app.Stack Overflow Solutions Here are a few approaches to fix this issue: 1. Use a unique key for each
useFetch
call
Then when calling the composable, provide a unique key:
2. Use $fetch
directly for mutations
If you're using this composable for form submissions or other mutations, consider using $fetch
directly instead of useFetch
. This is recommended in the Nuxt documentation and community:
When sending data, using useFetch is often not the way to go, especially on interactionGitHub Issue 3. Use
immediate: false
with manual execution
You could also use the immediate: false
option with useFetch
to have more control over when the fetch happens:
This approach is documented in the Not immediate section of the Nuxt docs.
Additional Considerations
- The issue in GitHub #31406 mentions a similar problem where they needed to prevent fetch from being triggered in certain states, which was resolved by using lazy
and immediate
options together.
- Remember that when using useFetch
with a computed URL (as you're doing), changing the reactive value will automatically trigger a refetch, as mentioned in the Computed URL documentation.
I hope this helps resolve your issue with the composable!
----------------------
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
----------------------<@77852149421191168> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
I'm sorry I forgot to add it in my example but it is there. I edited the original post. @kapa.ai
@kapa.ai
Let me check the knowledge base for you again. One moment! :books:
Sorry @hackal, I seem to be having a bit of trouble. Don't worry though, I've pinged my support and they will try to fix me up! :technologist:
Perhaps try again in 15mins?
@kapa.ai I have { immediate: false } there.
Let me check the knowledge base for you again. One moment! :books:
----------------------
I see that you have
{ immediate: false }
in your code. This is an important detail I missed in my previous analysis. Let me address how this affects your situation.
When using useFetch
with immediate: false
, the fetch won't automatically execute when the component mounts, but you'll need to manually call execute()
to trigger the request. However, this doesn't solve the key issue with multiple components using the same composable.
The main problem is still related to how useFetch
handles caching and key generation. When you use the same URL pattern in multiple components, Nuxt treats them as the same request and shares the data between them, even with immediate: false
.
According to the documentation:
If you do not provide a key, then a key that is unique to the file name and line number of the instance of useAsyncData
will be generated for you.
useAsyncData documentation
Since useFetch
is built on top of useAsyncData
, the same principle applies. However, when wrapping useFetch
in a custom composable, this automatic key generation may not work as expected across multiple component instances.
To fix this issue, you should explicitly provide a unique key for each useFetch
call:
Alternatively, you could pass a unique key as a parameter to your composable:
This way, each component can provide its own unique key when calling the composable, ensuring that the watchers work independently.
----------------------
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
----------------------<@77852149421191168> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
@kapa.ai I see, but how come I see two requests in network and both finish with 200. Only one of the useFetch composables updates the data.
Let me check the knowledge base for you again. One moment! :books:
----------------------
You're observing an interesting behavior with
useFetch
that relates to how Nuxt handles data fetching and caching. Let me explain what's happening.
When you see two network requests both completing with 200 status codes but only one instance of useFetch
updating its data, this is likely due to how Nuxt's caching mechanism works with the composables.
According to the documentation and issues in the knowledge sources:
1. When useFetch
is used in multiple places with the same URL pattern but different request bodies, Nuxt may still consider them the same request if they don't have unique keys. This is because the body is not part of the auto-generated key calculation:
No, the body is not part of auto-generated keys, only query params, method, base urlGitHub Issue 2. Even though both requests are being made (as you see in the network tab), Nuxt's internal caching mechanism might be causing only one of the watchers to trigger. 3. A similar issue was reported in a Stack Overflow post where someone had multiple components using the same composable with
useFetch
, and the data was being overwritten:
When ChildPage also called the same function, it somehow overrode the result, which unfortunately wasn't what TopBar asked for.Stack Overflow The solution is to ensure each
useFetch
call has a unique key. Even though you're using immediate: false
, you still need to differentiate the cache keys:
This ensures that each component has its own isolated cache entry, and both watchers will trigger independently when their respective data changes.
Another approach mentioned in the knowledge sources is to use Promise.all
to handle multiple fetch operations if they need to be coordinated:
This pattern was used in a Stack Overflow solution where multiple useFetch
calls were causing issues.
----------------------
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
----------------------<@77852149421191168> Kapa.ai is still learning and improving, please let me know how I did by reacting below.