Composables that depend on eachother

I have two composables that both use "useAsyncData" to fetch some firebase data. The first fetches an episode and the second fetches all episodes with similar tags to that episode. However when I run my code the episode is undefined when passed as an argument to the useSimilarEpisodes composable. I have tried adding an await to the first composable but this blocks navigation, and if I do useLazyAsyncData it doesn't have the desired effect of waiting for the episode to be defined (I have attached my code as pictures) Anyone have a clue how I could do this, perhaps another method without composables? Thanks for your time🙏
No description
No description
No description
6 Replies
kapa.ai
kapa.ai2mo ago
Hi @AxelSorensen. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- Based on the information provided in the knowledge sources, I can offer some insights and suggestions for your issue with composables that depend on each other using useAsyncData. The problem you're experiencing is likely related to how Nuxt handles async composables and the setup function. Here are a few potential solutions: 1. Use Promise.all: Instead of calling the composables separately, you can use Promise.all to ensure both async operations complete before proceeding. This approach was mentioned in a Stack Overflow post:
Promise.all([fetchEpisode(), fetchSimilarEpisodes(episode)])
.then(([episodeData, similarEpisodesData]) => {
// Handle the results here
})
.catch((error) => {
console.error(error);
});

Promise.all([fetchEpisode(), fetchSimilarEpisodes(episode)])
.then(([episodeData, similarEpisodesData]) => {
// Handle the results here
})
.catch((error) => {
console.error(error);
});

Stack Overflow Post (https://stackoverflow.com/questions/77949232) 2. Use runWithContext or callWithNuxt: These methods can help you run async operations within the correct Nuxt context. This was mentioned in a YouTube video about handling async code in Vue and Nuxt: YouTube: Nuxt 3: Nuxt Instance Unavailable - Async Code in Vue and Nuxt with the Composition API (https://www.youtube.com/watch?v=ofuKRZLtOdY) 3. Return the response from useAsyncData: Instead of awaiting the result immediately, you can return the promise from useAsyncData and handle it in the component where it's used. This allows you to chain the dependent calls:
const { data: episode } = await useAsyncData('episode', () => fetchEpisode())
const { data: similarEpisodes } = await useAsyncData('similarEpisodes', () =>
fetchSimilarEpisodes(episode.value)
)

const { data: episode } = await useAsyncData('episode', () => fetchEpisode())
const { data: similarEpisodes } = await useAsyncData('similarEpisodes', () =>
fetchSimilarEpisodes(episode.value)
)

Nuxt 3 Docs: useAsyncData (https://nuxt.com/docs/api/composables/use-async-data) 4. Use watch option: You can use the built-in watch option of useAsyncData to automatically rerun the fetcher function when the episode data changes:
const { data: episode } = await useAsyncData('episode', () => fetchEpisode())
const { data: similarEpisodes } = await useAsyncData(
'similarEpisodes',
() => fetchSimilarEpisodes(episode.value),
{ watch: [episode] }
)

const { data: episode } = await useAsyncData('episode', () => fetchEpisode())
const { data: similarEpisodes } = await useAsyncData(
'similarEpisodes',
() => fetchSimilarEpisodes(episode.value),
{ watch: [episode] }
)

Nuxt 3 Docs: useAsyncData - Watch Params (https://nuxt.com/docs/api/composables/use-async-data#watch-params) Remember that if you're not fetching data on the server (for example, with server: false), the data will not be fetched until hydration completes. This means even if you await useAsyncData on the client side, data will remain null within <script setup>. Nuxt 3 Docs: useAsyncData (https://nuxt.com/docs/api/composables/use-async-data) These approaches should help you manage the dependencies between your composables without blocking navigation or causing undefined values. Choose the method that best fits your specific use case and application structure. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai2mo ago
<@558317900943982593> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
zoobzio
zoobzio2mo ago
it sounds like there is a linear dependency between fetchEpisode and fetchSimilarEpisodes the simplest solution by far would be to combine the functions into a single useAsyncData wrapper that grabs both in sequence:
// ...
const ep = await getDoc(docRef);
if (ep.exists()) {
const eps = await getDocs(/* query here */);
return {
episode: { ep.id, ...ep.data(),
similarEpisodes: eps.docs.map(doc => doc.data())
}
}
// ...
// ...
const ep = await getDoc(docRef);
if (ep.exists()) {
const eps = await getDocs(/* query here */);
return {
episode: { ep.id, ...ep.data(),
similarEpisodes: eps.docs.map(doc => doc.data())
}
}
// ...
this is a rough example of what I mean, but in most cases you are better off providing a component a single entry point for useAsyncData that provides all of the objects needed, you will have a hard time if you try and compose data like that within the component itself as the component state will become a nightmare to manage
AxelSorensen
AxelSorensenOP2mo ago
Thanks a lot @zoobzio That's what I ended up doing
const { data: episodes } = useAsyncData(`episodes-from-season-${season_id}`, async () => {

// Fetch season document to get the episode IDs
const epDocRef = doc(db, 'seasons', season_id)
const ep_ids = await getDoc(epDocRef)
const ep_ids_ = ep_ids.data()?.episodes
// Query the episodes based on the episode IDs
const eps = await getDocs(query(collection(db, 'episodes'), where(documentId(), 'in', ep_ids_), orderBy('date', order)))

// Return the episodes data

return eps.docs.map(doc => ({ id: doc.id, ...doc.data() }))

}, {// Fetch data immediately
transform: (data) => {
return {
data,
fetchedAt: Date.now(),
}
},
getCachedData: (key) => {
const cachedData = nuxt.payload.data[key] || nuxt.static.data[key]
if (!cachedData) {
return
}
if (Date.now() - cachedData.fetchedAt > 1000 * 60) { // 1 minute cache
return
}
console.log('returning cache')
return cachedData
}
})
const { data: episodes } = useAsyncData(`episodes-from-season-${season_id}`, async () => {

// Fetch season document to get the episode IDs
const epDocRef = doc(db, 'seasons', season_id)
const ep_ids = await getDoc(epDocRef)
const ep_ids_ = ep_ids.data()?.episodes
// Query the episodes based on the episode IDs
const eps = await getDocs(query(collection(db, 'episodes'), where(documentId(), 'in', ep_ids_), orderBy('date', order)))

// Return the episodes data

return eps.docs.map(doc => ({ id: doc.id, ...doc.data() }))

}, {// Fetch data immediately
transform: (data) => {
return {
data,
fetchedAt: Date.now(),
}
},
getCachedData: (key) => {
const cachedData = nuxt.payload.data[key] || nuxt.static.data[key]
if (!cachedData) {
return
}
if (Date.now() - cachedData.fetchedAt > 1000 * 60) { // 1 minute cache
return
}
console.log('returning cache')
return cachedData
}
})
However now I am facing an issue with the caching.
AxelSorensen
AxelSorensenOP2mo ago
I want to cache the data so I don't need to refetch it on each page navigation. However much of the data fetched from my composables relies on the current user. I do "await getCurrentUser" before passing this value into many of my functions, however I want to clear all my caches and refetch when a user signs in or out. Usually I would use refreshNuxtData but it seems to me that this only works with data in the same scope: https://github.com/nuxt/nuxt/issues/26165 Having to export a refresh function from each of my useasyncdata composables and then running them all at the same time seems cumbersome Is there perhaps a better way?
GitHub
refreshNuxtData doesn't re-fetch data when useAsyncData/`useFet...
Environment Operating System: Windows_NT Node Version: v21.5.0 Nuxt Version: 3.10.3 CLI Version: 3.10.1 Nitro Version: 2.8.1 Package Manager: [email protected] Builder: - User Config: ssr, devtools, modu...
zoobzio
zoobzio2mo ago
is there any issue w/ using clearNuxtData on logout? https://nuxt.com/docs/api/utils/clear-nuxt-data if the user logs back in during the same session, they would refetch any relevant data as they navigate just like they would on first page load because the data has been cleared I guess I don't understand a use case where page data would need to be refreshed on login outside of navigating back to that page, it makes more sense to treat a login action as a fresh application load & apply the same rules it's also really only relevant in situations where a user is logged in & opens a page, then logs out, then logs back in without reloading the app, then returns to a page expecting data to be present. far simpler to just make that very specific use case wait for their data again, no?
Nuxt
clearNuxtData · Nuxt Utils
Delete cached data, error status and pending promises of useAsyncData and useFetch.
Want results from more Discord servers?
Add your server