I’m using Vue 3 with Nuxt 3 and Vite, and I’m encountering a strange bug. It occurs almost everywhere in my app whenever I try to use useFetch within a composable.
For example, I have the following composable to check whether a button can be rendered based on the user’s role:
useCheckerRole Composable
export function useCheckerRole(policies){
const {data, error} = await useFetch("/api/role-checker", {
method: "POST",
body: policies
})
if(data.value) {
return data.value
}
if(error.value) {
throw new Error("an error occured");
}
}
I then need to pass the policies like so whenever I need to check their roles.
const policies = await useCheckerRole([
{
component_ask: 'some-button',
role_ask: 'administrator'
}
])
which then returns something like this from the api:
[{
component_ask: 'some-button',
role_ask: 'administrator',
status: 'approved'
}]
The Bug
The bug shows up when I mount useCheckerRole in two different components on the same page.
For instance, in my topbar component, I use the useCheckerRole to determine if a specific button or menu should be displayed, depending on the user’s role. Meanwhile, elsewhere on the page, I have another button that requires similar role-based checking.
TopBar.vue
The topbar sends this to the role checker and then immediately logs the result
const policies = await useCheckerRole([
{
component_ask: 'top-bar-button',
role_ask: 'administrator'
}
]).catch((error)....)
console.log("topBar Policies result", policies)
ChildPage.vue
The child page sends this to the useCheckerRole and immediately logs the result
const policies = await useCheckerRole([
{
component_ask: 'child-page-button',
role_ask: 'administrator'
},
{
component_ask: 'child-page-other-button',
role_ask: 'administrator'
},
]).catch((error)....)
console.log("Child Policies result", policies)
TopBar.vue Result
now the result of the Topbar is interestingly this:
π What TopBar got
[
{
component_ask: 'child-page-button',
role_ask: 'administrator',
status: "approved"
},
{
component_ask: 'child-page-other-button',
role_ask: 'administrator',
status: "rejected"
},
]
Notice that this was not what the Topbar component sent to the useCheckerRole, rather this was what the ChildPage sent.
β What TopBar should’ve gotten
[
{
component_ask: 'top-bar-button',
role_ask: 'administrator',
status: "approved"
},
]
The ChildPage also got the same result as the Topbar:
[
{
component_ask: 'child-page-button',
role_ask: 'administrator',
status: "approved"
},
{
component_ask: 'child-page-other-button',
role_ask: 'administrator',
status: "rejected"
},
]
This made me realize that composables are not as isolated as I thought when using useFetch, or maybe I’m doing something very wrong. Because it looks like both component were calling to the same unisolated function at the same time even if said function are supposed to be isolated.
The initial result in TopBar was correct. However, when ChildPage also called the same function, it somehow overrode the result, which unfortunately wasn’t what TopBar asked for.
Does anyone else have this issue?
2
Answers
After playing left and right with the solutions that Yue JIN and Estus Flask gave, I finally found the problem. I stumbled across this article - Nuxt 3 useFetch β Are you using it correctly? and read the following statement from the author:
Within the article, he demonstrated how the bug occured after wrapping useFetch within a function. Immediately after seeing this, I saw that my function was indeed wrapping a composable that's wrapping the useFetch composable.
πThe Code that Lead to Bug
TopBar/ChildPage Component
that useCheckerRole is a composable within a function that uses useFetch. So the weird bug indeed appeared.
β My Solution
lib/roleChecker.ts
I decided to test this by creating a normal function and placed it outside of the composables folder; in a folder called lib.
Inside of the roleChecker.ts, I no longer use useFetch. Instead, I use $fetch to call my api and return them.
Now for each of my component I can finally call the roleChecker within any function and the bugs no longer bother me.
TopBar/ChildPage
I think it is not due to ‘isolation’, but cache of
useFetch
.useFetch
useskey
to prevent refetching the same data. By default thekey
is the provided URL which is the same in your two components. Can you try assigning a different key for them?