N
Nuxt4mo ago
JV

moving from Nuxt2 useFetch to Nuxt3 useAsyncData - how would you refactor this example?

Hello, as a title says, how would you refactor this example to keep SSR working exactly 1:1? Nuxt 2
const homePage = ref<HomePage | null>(null)
const homePageLoaded = ref<boolean>(false)

useFetch(async () => {
homePage.value = await loadHomePage()
homePageLoaded.value = true
})
const homePage = ref<HomePage | null>(null)
const homePageLoaded = ref<boolean>(false)

useFetch(async () => {
homePage.value = await loadHomePage()
homePageLoaded.value = true
})
24 Replies
JV
JV4mo ago
I tried to refactor it this way:
const homePageLoaded = ref<boolean>(false)

const { data: homePage } = useAsyncData(async () => {
homePage.value = await loadHomePage()
homePageLoaded.value = true

return homePage
})
const homePageLoaded = ref<boolean>(false)

const { data: homePage } = useAsyncData(async () => {
homePage.value = await loadHomePage()
homePageLoaded.value = true

return homePage
})
But the homePageLoaded is not synced properly and I dont think using useState for this is a good way... could you please help/explain, how to refactor it with the smallest amount of other code changes possible?
Wild
Wild4mo ago
Not a expert, but I think something like this should work
const { data: homePage, pending: homePageLoaded } = useAsyncData(async () => {
return await loadHomePage()
})
const { data: homePage, pending: homePageLoaded } = useAsyncData(async () => {
return await loadHomePage()
})
JV
JV4mo ago
Do not rely on variable name or usage, the main issue is how to modify other refs inside useAsyncData
Wild
Wild4mo ago
I am not to sure about your goal, but maybe take a look at the onReponse param ?
const { data, pending, error, refresh } = await useFetch('/api/auth/login', {
onRequest({ request, options }) {
// Set the request headers
options.headers = options.headers || {}
options.headers.authorization = '...'
},
onRequestError({ request, options, error }) {
// Handle the request errors
},
onResponse({ request, response, options }) {
// Process the response data
localStorage.setItem('token', response._data.token)
},
onResponseError({ request, response, options }) {
// Handle the response errors
}
})
const { data, pending, error, refresh } = await useFetch('/api/auth/login', {
onRequest({ request, options }) {
// Set the request headers
options.headers = options.headers || {}
options.headers.authorization = '...'
},
onRequestError({ request, options, error }) {
// Handle the request errors
},
onResponse({ request, response, options }) {
// Process the response data
localStorage.setItem('token', response._data.token)
},
onResponseError({ request, response, options }) {
// Handle the response errors
}
})
otherwise myvar.value seems like the way
BeerDuck
BeerDuck4mo ago
@JV Do you mean your homePageLoaded.value is at the end still false?
Wild
Wild4mo ago
also note the await before the UseFetch
JV
JV4mo ago
@BeerDuck exactly!
manniL
manniL4mo ago
why not const { data, status } = await useFetch() and check if status is PENDING? ah, saw this ideally you don't a watcher based on data (or status or similar) would be better
BeerDuck
BeerDuck4mo ago
I try to explain what happens, and maybe it is not 100% correct but. Your code within your asyncdata will not be executed a second time. because the whole function gets skipped, and the data: homepage gets filled in without executing the function again. This is why everything that happens within the function does not get executed a second time. What i think you just want is an normal function around it. const homePageLoaded = ref(false); const homePage = ref(1); async function loaddata(){ const{ data: thedata } = useAsyncData( async () => { return await loadHomePage(); }); homePage.value=thedata; homePageLoaded.value = true; } await loaddata(); btw above code isnt optimal or directly logical to use in any situation in my mind, but i think this solves your issue. And looks mostly like your old code
JV
JV4mo ago
Ok, and one more question - does useAsyncData have some way how to add event listeners like onRequest like you showed in your example with useFetch? @Wild
BeerDuck
BeerDuck4mo ago
watch(() => watachablevalue, (newVal,oldVal) => { //use variable or do something when variable changes. }); This keeps track on variable changes if i am correct.
Wild
Wild4mo ago
useFetch being a shorthand for useAsyncData, it should accept exactly the same parameters, although I can't confirm this will 100% work it should
JV
JV4mo ago
@manniL / TheAlexLichter sorry for mention, but only you could propably know if this could be a problematic usage? I know its not ideal usage, but its acrually working - can this lead to some unexpected behavior, or it is safe to use? I have some purposes why I need to use it somehow like this (rewritting old nuxt2 project into nuxt3 with as little changes as possible accross whole project)
No description
manniL
manniL4mo ago
no, useAsyncData in useAsyncData is not okay It'd help explain "some purposes" 🙂
JV
JV4mo ago
@manniL / TheAlexLichter thank you for your answer! Ill try to explain "some purposes" we are using "useFetch" with our wrapper, which provides some events like "onSuccess", "onError" etc. In some cases, we need to modify data that comes from api call - this maybe could be achieved by computed properties. But I would like to keep our wrapper with "onSuccess", "onError" events
manniL
manniL4mo ago
"we need to modify data" -> how about using transform? or doing that in asyncData after the fetching the call from an API?
JV
JV4mo ago
I maybe overengineered that, now it looks clear, Ill try to do my best and in case of any other problems Ill let you know - if I can. Thank you so much!
manniL
manniL4mo ago
no problem 👍
JV
JV4mo ago
@manniL / TheAlexLichter how would you implement these functions like "onSuccess"/"onError"? We are using "ste-simple-events" package for that
manniL
manniL4mo ago
Hard to tell without a bit more code. but probably in your $fetch (as it has interceptor support)
JV
JV4mo ago
Im now working with this in mind - trying to create composable which allows me to fetch data of HomePage on SSR and on client either, when I come from another subpage. This is just example in my mind, but I dont think this will be ok too.. @manniL / TheAlexLichter - this immediate: false and calling execute is important for me - Im executing it from another composable
export const useHomePage = async () => {

const onSuccess = new SimpleEventDispatcher()
const onError = new SimpleEventDispatcher()

const {data, execute} = useAsyncData(async () => {
try {
const response = await doSomething()
onSuccess.dispatch(response)
return response
} catch (e) {
onError.dispatch(e)
}
}, {immediate: false})

await execute()

return {
onSuccess,
onError,
data,
execute
}
}
export const useHomePage = async () => {

const onSuccess = new SimpleEventDispatcher()
const onError = new SimpleEventDispatcher()

const {data, execute} = useAsyncData(async () => {
try {
const response = await doSomething()
onSuccess.dispatch(response)
return response
} catch (e) {
onError.dispatch(e)
}
}, {immediate: false})

await execute()

return {
onSuccess,
onError,
data,
execute
}
}
manniL
manniL4mo ago
good point! besides that: execute and immediate false is a bit strange. You could just do a refresh which comes from the useAsyncData and return the whole composable "response object" instead
JV
JV4mo ago
isnt execute and refresh the same functionallity?
manniL
manniL4mo ago
yes