N
Nuxt3d ago
Kyllian

Hydration error I can not figure out

Currently, this page displays a map with markers on it. When clicking a marker it (should) take me to a different url. I use navigateTo and the URL itself gets changed just fine. It seems like it simply does not update my specificLocation:
const slug = computed(() => route.params.slug);

const specificLocation = ref();
watch(slug, async () => {
console.log("slug changed", slug.value);
if (slug.value.length === 3) {
const { data: location } = await useLazyFetch(`/api/v2/locations/search`, {
params: {
countryCode: slug.value[0],
cityName: slug.value[1],
locationName: slug.value[2],
}
});

console.log("Location updated")
specificLocation.value = location.value;
}
}, {immediate: true})
const slug = computed(() => route.params.slug);

const specificLocation = ref();
watch(slug, async () => {
console.log("slug changed", slug.value);
if (slug.value.length === 3) {
const { data: location } = await useLazyFetch(`/api/v2/locations/search`, {
params: {
countryCode: slug.value[0],
cityName: slug.value[1],
locationName: slug.value[2],
}
});

console.log("Location updated")
specificLocation.value = location.value;
}
}, {immediate: true})
<div v-if="slug !== ''" class="w-full">
<div v-if="specificLocation">
<h1 class="text-white text-left w-full font-bold text-xl">{{ specificLocation.location.name }}
</h1>
<!-- <h2 class="text-gray-300 text-left w-full text-sm">{{ specificLocation.cityName }}</h2>
<ClientOnly>
<p id="richtext" class="text-white text-left w-full mt-2" v-html="description"></p>
</ClientOnly> -->
</div>
<div v-else>
Loading...
</div>
</div>
<div v-if="slug !== ''" class="w-full">
<div v-if="specificLocation">
<h1 class="text-white text-left w-full font-bold text-xl">{{ specificLocation.location.name }}
</h1>
<!-- <h2 class="text-gray-300 text-left w-full text-sm">{{ specificLocation.cityName }}</h2>
<ClientOnly>
<p id="richtext" class="text-white text-left w-full mt-2" v-html="description"></p>
</ClientOnly> -->
</div>
<div v-else>
Loading...
</div>
</div>
It simply keeps in the loading state, what could this be.
5 Replies
kapa.ai
kapa.ai3d ago
Hi @Kyllian. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- Based on your code and description, you're experiencing a hydration issue in your Nuxt application. This is likely related to how reactive values are being handled during server-side rendering and client-side hydration. The Problem The issue appears to be with how useLazyFetch is being used within a watch callback. When navigating to a different URL, the slug changes, but your component remains in the loading state because the data fetching process isn't properly synchronized with Nuxt's hydration lifecycle. Possible Causes 1. Hydration Mismatch: In Nuxt, pages are rendered twice - first on the server, then hydrated on the client. If data differs between these phases, you'll get hydration issues Stack Overflow Post > Nuxt 3 Hydration text mismatch. 2. Reactive URL Issue: When using useLazyFetch with reactive values, you need to ensure the URL or parameters update properly when dependencies change Docs > Getting-started > Data Fetching. 3. Watch Timing: The watch callback might be executing at a time that conflicts with Nuxt's hydration process. Solutions Here are some approaches to fix this issue: 1. Use Computed URL Pattern Instead of using watch with useLazyFetch inside, directly use the reactive pattern with useLazyFetch:
const slug = computed(() => route.params.slug);

const { data: specificLocation, status } = useLazyFetch(() => {
if (slug.value.length === 3) {
return `/api/v2/locations/search?countryCode=${slug.value[0]}&cityName=${slug.value[1]}&locationName=${slug.value[2]}`;
}
return null;
}, {
immediate: true,
watch: [slug] // This will automatically refetch when slug changes
});
const slug = computed(() => route.params.slug);

const { data: specificLocation, status } = useLazyFetch(() => {
if (slug.value.length === 3) {
return `/api/v2/locations/search?countryCode=${slug.value[0]}&cityName=${slug.value[1]}&locationName=${slug.value[2]}`;
}
return null;
}, {
immediate: true,
watch: [slug] // This will automatically refetch when slug changes
});
Then update your template to use the status:
<div v-if="slug !== ''" class="w-full">
<div v-if="status === 'success' && specificLocation">
<h1 class="text-white text-left w-full font-bold text-xl">
{{ specificLocation.location.name }}
</h1>
</div>
<div v-else>
Loading...
</div>
</div>
<div v-if="slug !== ''" class="w-full">
<div v-if="status === 'success' && specificLocation">
<h1 class="text-white text-left w-full font-bold text-xl">
{{ specificLocation.location.name }}
</h1>
</div>
<div v-else>
Loading...
</div>
</div>
2. Use useFetch with Proper Query Parameters Alternatively, you can use the query parameter approach:
const slug = computed(() => route.params.slug);
const canFetch = computed(() => slug.value.length === 3);

const { data: specificLocation } = useLazyFetch('/api/v2/locations/search', {
query: {
countryCode: computed(() => canFetch.value ? slug.value[0] : null),
cityName: computed(() => canFetch.value ? slug.value[1] : null),
locationName: computed(() => canFetch.value ? slug.value[2] : null)
},
immediate: false,
watch: [canFetch]
});
const slug = computed(() => route.params.slug);
const canFetch = computed(() => slug.value.length === 3);

const { data: specificLocation } = useLazyFetch('/api/v2/locations/search', {
query: {
countryCode: computed(() => canFetch.value ? slug.value[0] : null),
cityName: computed(() => canFetch.value ? slug.value[1] : null),
locationName: computed(() => canFetch.value ? slug.value[2] : null)
},
immediate: false,
watch: [canFetch]
});
This approach leverages Nuxt's ability to automatically refetch when reactive query parameters change Docs > Getting-started > Data Fetching. 3. Check for Hydration State You might want to check if the app is currently hydrating:
const nuxtApp = useNuxtApp();
if (!nuxtApp.isHydrating) {
// Execute code only after hydration is complete
}
const nuxtApp = useNuxtApp();
if (!nuxtApp.isHydrating) {
// Execute code only after hydration is complete
}
This can help avoid executing certain code during the hydration phase Docs > Api > Composables > useNuxtApp. Additional Considerations - Make sure you're handling the loading state correctly by checking both for the existence of data and the status of the fetch operation. - Consider using useLazyFetch instead of useFetch if you want navigation to proceed without waiting for data (which seems to be your current approach). - Be careful with modifying props or reactive data during the hydration phase, as this can cause hydration mismatches. By implementing one of these approaches, you should be able to resolve the hydration issue and get your location data loading correctly when navigating between URLs. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai3d ago
<@166602211780984832> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
Cake
Cake3d ago
@Kyllian specificLocation needs to be a useState, not a ref https://nuxt.com/docs/getting-started/state-management
Kyllian
KyllianOP3d ago
hm i see i did it like this now:
const { data: specificLocation, status } = useLazyFetch<{location: ILocation, cityName: string}>(() => {
if (slug.value.length === 3) {
return `/api/v2/locations/search?countryCode=${slug.value[0]}&cityName=${slug.value[1]}&locationName=${slug.value[2]}`;
}
return '';
}, {
immediate: true,
watch: [slug]
});
const { data: specificLocation, status } = useLazyFetch<{location: ILocation, cityName: string}>(() => {
if (slug.value.length === 3) {
return `/api/v2/locations/search?countryCode=${slug.value[0]}&cityName=${slug.value[1]}&locationName=${slug.value[2]}`;
}
return '';
}, {
immediate: true,
watch: [slug]
});
this also fixes it, not entirely sure which solution is best, though
manniL
manniL2d ago
you should not use useLazyFetch in watch btw. that seems to be a better approach.

Did you find this page helpful?