N
Nuxt3mo ago
Revadike

Lazy data caching issue (reactivity)

I've tried so many things, but unless getTotalTradesWithUser's argument is an actual static value, it refuses to load the cached data and refetches it every time.
const getUserId = () => props.userId;
const uid = `${getUserId()}`; // caching refuses to work...
// const uid = '8ed79db8-8d2a-4a95-a6d0-a55234e98f4fe'; // ...but this does
const { data: tradesCommon, status: tradesStatus, error: tradesError } = useLazyAsyncData(`user-trades-with-${props.userId}`, () => {
if (!isLoggedIn || isMe.value || props.userId === null) {
return null;
}

const user = new User(authUser.id);
return user.getTotalTradesWithUser(uid);
}, {
getCachedData: (key, nuxtApp) => {
return nuxtApp.payload.data[key] || nuxtApp.static.data[key];
}
});
const getUserId = () => props.userId;
const uid = `${getUserId()}`; // caching refuses to work...
// const uid = '8ed79db8-8d2a-4a95-a6d0-a55234e98f4fe'; // ...but this does
const { data: tradesCommon, status: tradesStatus, error: tradesError } = useLazyAsyncData(`user-trades-with-${props.userId}`, () => {
if (!isLoggedIn || isMe.value || props.userId === null) {
return null;
}

const user = new User(authUser.id);
return user.getTotalTradesWithUser(uid);
}, {
getCachedData: (key, nuxtApp) => {
return nuxtApp.payload.data[key] || nuxtApp.static.data[key];
}
});
No description
8 Replies
kapa.ai
kapa.ai3mo ago
Hi @Revadike. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- It’s likely two things combined: 1) Your key is reactive and changing, so Nuxt treats each call as different data and refetches. With Nuxt 4’s singleton data layer, calls with the “same” key share refs and cache, but only if the key is exactly the same string (or a stable reactive key). Prefer a ref/computed key and pass it directly so changes are tracked properly. [Singleton layer; Reactive keys (API); useLazyAsyncData reactive keys fix] 2) Your getCachedData default is too narrow. The newer default returns cached data from payload first, then static data, except on manual refresh/refresh hook, which avoids unnecessary refetches. You can mirror that logic. [Experimental features: granularCachedData; Default getCachedData change; Upgrade guide details] Try: - Make the key reactive but stable, and ensure the handler depends on the same reactive source. - Return cached data using the updated default behavior. Example:
<script setup lang="ts">
const userId = computed(() => props.userId)

// Stable, reactive key
const key = computed(() => `user-trades-with-${userId.value}`)
const { data: tradesCommon, status: tradesStatus, error: tradesError } = await useLazyAsyncData(
key,
async () => {
if (!isLoggedIn || isMe.value || userId.value == null) {
// Return a truthy default if you want to avoid duplicate requests
return []
}
const user = new User(authUser.id)
return user.getTotalTradesWithUser(String(userId.value))
},
{
// Updated cache strategy: payload first, then static, unless manually refreshed
getCachedData: (k, nuxtApp, ctx) => {
if (ctx.cause === 'refresh:manual' || ctx.cause === 'refresh:hook') return undefined
return nuxtApp.payload.data[k] ?? nuxtApp.static.data[k]
}
}
)
</script>
<script setup lang="ts">
const userId = computed(() => props.userId)

// Stable, reactive key
const key = computed(() => `user-trades-with-${userId.value}`)
const { data: tradesCommon, status: tradesStatus, error: tradesError } = await useLazyAsyncData(
key,
async () => {
if (!isLoggedIn || isMe.value || userId.value == null) {
// Return a truthy default if you want to avoid duplicate requests
return []
}
const user = new User(authUser.id)
return user.getTotalTradesWithUser(String(userId.value))
},
{
// Updated cache strategy: payload first, then static, unless manually refreshed
getCachedData: (k, nuxtApp, ctx) => {
if (ctx.cause === 'refresh:manual' || ctx.cause === 'refresh:hook') return undefined
return nuxtApp.payload.data[k] ?? nuxtApp.static.data[k]
}
}
)
</script>
Notes: - Returning null/undefined from the handler can cause duplicate fetches; prefer a default value via options.default or return a truthy value. [useAsyncData params] - If you need refetch on certain changes, use watch or reactive keys explicitly; otherwise the cache will be reused. [Computed URL/watch; Watch params] - Reactive key support in useLazyAsyncData is fixed; ensure you’re on a version including that PR. [useLazyAsyncData reactive keys fix] ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai3mo ago
<@172781195329798144> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
Revadike
RevadikeOP3mo ago
@kapa.ai Nah, it works perfectly fine if I just make the function argument static. The data key isn't the problem here.
kapa.ai
kapa.ai3mo ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- Thanks for the clarification. If the handler only refetches when its argument is reactive (but not when it’s a static string), a likely cause is that the handler sometimes returns null/undefined, which Nuxt treats as “no data” and can lead to duplicate or repeated fetches rather than using cache. The docs note the handler must return a truthy value to avoid duplicated requests. [useAsyncData params] What to try: - Ensure the handler always returns a truthy value (or provide options.default) even when you want to “skip”:
const { data, status, error } = await useLazyAsyncData(
`user-trades-with-${props.userId}`,
async () => {
if (!isLoggedIn || isMe.value || props.userId == null) {
return [] // truthy fallback instead of null
}
const user = new User(authUser.id)
return user.getTotalTradesWithUser(String(props.userId))
}
)

const { data, status, error } = await useLazyAsyncData(
`user-trades-with-${props.userId}`,
async () => {
if (!isLoggedIn || isMe.value || props.userId == null) {
return [] // truthy fallback instead of null
}
const user = new User(authUser.id)
return user.getTotalTradesWithUser(String(props.userId))
}
)

Returning null can cause duplicated fetch behavior. [useAsyncData params] - Consider using the updated default getCachedData behavior (payload first, then static, except on manual refresh) to avoid unnecessary refetches:
{
getCachedData: (key, nuxtApp, ctx) => {
if (ctx.cause === 'refresh:manual' || ctx.cause === 'refresh:hook') return undefined
return nuxtApp.payload.data[key] ?? nuxtApp.static.data[key]
}
}

{
getCachedData: (key, nuxtApp, ctx) => {
if (ctx.cause === 'refresh:manual' || ctx.cause === 'refresh:hook') return undefined
return nuxtApp.payload.data[key] ?? nuxtApp.static.data[key]
}
}

[getCachedData change] - If you’re on an older version, ensure useLazyAsyncData supports reactive keys properly (fixed in PR 32092). [Reactive keys fix] Also remember: with the singleton data layer, calls with the same key share refs and cache; option inconsistencies like different transform/pick/default across calls with that key can affect behavior. [Singleton layer] ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai3mo ago
<@172781195329798144> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
Revadike
RevadikeOP3mo ago
Updated to latest nuxt 4 version, still not working ok, I figured it out... it's because getTotalTradesWithUser was returning 0 (falsy) :/ there should be a better way to handle cache instead of checking for falsy values, i think it should explicitly check for undefined instead @TheAlexLichter
manniL
manniL3mo ago
I think this should be the case in Nuxt 4 already 👀 Can you raise an issue?
Revadike
RevadikeOP3mo ago
you think? are you not sure? I believe the docs also say it cannot be falsy

Did you find this page helpful?