Reputation: 53
I'm using Nuxt 3 + Pinia + FirebaseStore in my project and when I wrap my load data functions from Firebese by the AsyncData function I started getting an error "Hydration children mismatch in ". I didn't manage to find a good example of the nuxt 3 app with ssr or a solution for how to fix this warning or good example of nuxt 3 app with ssr or a solution how to fix this.
In Layout component, I'm getting the error from Footer.
And in the page section from ArticleLisctBlock component.
Layout default component:
<script setup lang="ts">
import { useAuthStore } from "@/stores/authStore";
import { storeToRefs } from "pinia";
//Get Authorized user
const { isAuthorized } = useAuthStore();
const { statAuth } = storeToRefs(useAuthStore());
//Fetch AboutUs data
const { aboutUs } = storeToRefs(useAboutUsStore());
const { getAboutUs } = useAboutUsStore();
//Fetch Contactsr links data
const { stateData } = storeToRefs(useContactStore());
const { getContacts } = useContactStore();
//Fetch Nav and Footer links data
const { navLinks, footerLinks } = storeToRefs(useNavStorage());
const { getList, getFooterList } = useNavStorage();
//Fetch Articles data
const { postsState } = storeToRefs(useArticleStore());
const { getPostList } = useArticleStore();
const { imageList } = storeToRefs(useGalaryStore());
const { getGalaryDBList } = useGalaryStore();
//CategoryLinks
const { categoryState } = storeToRefs(useCategoryStorage());
const { getGategoryList } = useCategoryStorage();
//Fetch Podcast List
const { podcastsState } = storeToRefs(usePodcastsStore());
const { getPodCastList } = usePodcastsStore();
//Fetch Advertisement List
const { advertiseList } = storeToRefs(useAdvertiseStore());
const { getAdvertiseList } = useAdvertiseStore();
async function loadStores2() {
await getPostList();
await getFooterList();
await getPodCastList();
await getList();
await getGategoryList();
await getGalaryDBList();
await getAdvertiseList();
await getAboutUs();
await getContacts();
}
await useAsyncData("loadData", loadStores2);
onMounted(async () => {
//check auth user in firebase
await isAuthorized();
});
</script>
<template>
<div class="defaultLayout">
<div class="header_block">
<UiAddTopHeader v-if="footerLinks" :category-links="navLinks" :top-links="footerLinks" />
<UiAddHeaderMiddle />
<UiAddHeader v-if="navLinks" :nav-links="navLinks" />
</div>
<div class="body_block">
<slot />
</div>
<div class="footer_block">
<UiAddFooter
v-if="footerLinks?.length && postsState?.postList?.length"
:about-us-links="footerLinks"
:favorites="postsState?.favoriteList"
:categories="categoryState" />
</div>
</div>
</template>
Footer component.
<script setup lang="ts">
import type { IArticle } from "types/IArticle";
import type { ICategory } from "types/ICategory";
import type { INavigation } from "types/INavigation";
defineProps({
categories: {
type: Array as PropType<ICategory[]>,
default: null,
},
aboutUsLinks: {
type: Array as PropType<INavigation[]>,
default: null,
},
favorites: {
type: Array as PropType<IArticle[]>,
default: null,
},
});
</script>
<template>
<div class="footer_container">
<img class="footer_image" src="/images/footer.jpg" alt="footer" />
<div class="content">
<div class="media_block grid_block">
<h2 class="media_block_title">WORLD IMPULSE</h2>
<p>© Munich, LLC. All rights reserved, LLC</p>
<UiAddSocialMediaList :is-inline-block="true" />
</div>
<div class="about_block grid_block">
<h2 class="title_block">About Us</h2>
<UiAddAboutLinks
v-if="aboutUsLinks"
direction="flex"
gaps="20px"
:aboutlinks="aboutUsLinks" />
</div>
<div class="popular_block grid_block">
<h2 class="title_block">Popular Category</h2>
<UiAddAboutLinks
v-if="categories?.length"
direction="grid"
gaps="20px"
:categorylinks="categories?.slice(1, 8)" />
</div>
<div class="editor_picks grid_block">
<h2 class="title_block">Editor Piks</h2>
<ArticleSingleArticle
class-type="image_content_rigth"
:show-title="true"
:show-image="true"
font-size="16px"
:font-weight="400"
v-for="(el, i) in favorites.slice(0, 2)"
:key="i"
:single-post="el" />
</div>
</div>
</div>
</template>
[Id],page where ArticleListBlock component calls
<script setup lang="ts">
import { storeToRefs } from "pinia";
import type { IArticle } from "types/IArticle";
//Get Route params
const route = useRoute();
const routeSlug = String(route.params.slug);
const routeId = String(route.params.id);
//Fetch Articles data
const { postsState } = storeToRefs(useArticleStore());
const { isExistPost } = useArticleStore();
//Get Category data
const categoryStore = useCategoryStorage();
const { isExistCategory } = categoryStore;
// Check route params
const isRouteCorrect = () => {
if (routeSlug === "search") {
return true;
} else if (routeId && (routeId === "list" || isExistPost(routeId))) {
return true;
}
return false;
};
//Throw an error id route params are not correct
if (!isRouteCorrect()) {
throw createError({ statusCode: 404, statusMessage: `The Page not found` });
}
</script>
<template>
<div class="wrapper">
<ArticleListBlock v-if="route.params.slug === 'search' || (routeId && routeId === 'list')" />
<ArticleSingleBlock v-else-if="routeId && isExistPost(routeId)" />
</div>
</template>
ArticleListBlock component
<script setup lang="ts">
import type { NuxtError } from "nuxt/app";
import { storeToRefs } from "pinia";
import type { IArticle } from "types/IArticle";
const route = useRoute();
const articlesByCategory = ref<IArticle[]>([]);
const search = ref("");
const errorResponse = ref<NuxtError>();
// Articles state
const { postsState } = storeToRefs(useArticleStore());
const { getArticlesByCategory, findArticlesByName } = useArticleStore();
// Advertisement galary state
const { advertiseList } = storeToRefs(useAdvertiseStore());
if (route.params.slug === "search") {
search.value = String(route.params.id);
console.log(search.value);
}
// Getting data by search request
const searchComputed = computed(() => {
if (!search.value) {
route.params.slug === "search" && (articlesByCategory.value = postsState.postList);
route.params.id === "list" &&
(articlesByCategory.value = getArticlesByCategory(String(route.params.slug)));
return articlesByCategory.value;
} else {
articlesByCategory.value = findArticlesByName(search.value);
if (!articlesByCategory.value.length) {
errorResponse.value = createError({ statusCode: 404, statusMessage: "Not found results" });
}
return articlesByCategory.value;
}
});
useHead({
title: `${String(route.params.slug)} list`,
meta: [
{ name: `${String(route.params.slug)}`, content: `${String(route.params.slug)} daily news` },
],
});
</script>
<template>
<div class="list_container grid_block">
<div class="top grid_block">
<div class="advertise_block">
<UiElementsAdvertise label="Advertisement" :link="advertiseList.databaseList[1]" />
</div>
<div class="topic grid_block">
<h5 class="category">CATEGORY</h5>
<h1 class="title_category">{{ String(route?.params?.slug)?.toLocaleUpperCase() }}</h1>
<h4 class="info">
Read the latest news with the Best WordPress News Theme – Newspaper by Sergio Belov!
</h4>
</div>
<div class="form_block">
<UiElementsAddPostInput
label="Search"
width-form="100%"
font-size="2rem"
name="search"
placeholder="Input search"
v-model:value.trim="search" />
</div>
</div>
<div class="main_section">
<div class="list grid_block" v-if="searchComputed.length">
<div class="item" v-for="(el, i) in searchComputed" :key="i">
<ArticleSingleArticle
class-type="image_content_left"
:show-image="true"
:show-title="true"
:show-short-body="true"
:show-date="true"
:single-post="el"
:id="el?.id" />
</div>
</div>
<ErrorResponse v-else-if="errorResponse" :error-event="errorResponse" />
<div class="right_bar" v-if="postsState.postList.length">
<h3>Latest News</h3>
<ArticleSingleArticle
class-type="image_content_rigth"
:show-title="true"
:show-image="true"
:show-date="true"
v-for="(el, i) in postsState.postList?.slice(4, 7)"
:key="i"
:single-post="el" />
</div>
</div>
</div>
</template>
ArticleSingleArticle component from with calls from ArticleListBlock
<script setup lang="ts">
import type { IArticle } from "types/IArticle";
defineProps({
singlePost: {
type: Object as PropType<IArticle>,
default: null,
},
fontSize: {
type: String,
default: "18px",
},
fontWeight: {
type: Number,
default: 600,
},
rowSize: {
type: String,
default: "auto",
},
showContent: {
type: Boolean,
default: false,
},
showShortBody: {
type: Boolean,
default: false,
},
showCategory: {
type: Boolean,
default: false,
},
showTitle: {
type: Boolean,
default: false,
},
showImage: {
type: Boolean,
default: false,
},
showDate: {
type: Boolean,
default: false,
},
classType: {
type: String,
default: "",
},
});
</script>
<template>
<div class="single_post" :class="classType">
<div class="tag" v-if="showCategory">
<h5>{{ singlePost?.category?.toLocaleUpperCase() }}</h5>
</div>
<div class="title_post" v-if="showTitle">
<NuxtLink :to="{ path: `/${singlePost?.category}/${singlePost?.id}` }">
<p>{{ singlePost?.title }}</p>
</NuxtLink>
</div>
<div class="preview_image" v-if="showImage">
<img class="image" :src="singlePost?.image" alt="postPriview" />
</div>
<div v-if="showShortBody" class="shortn_block">
<p>{{ singlePost?.shortBody }}</p>
</div>
<div class="post_created" v-if="showDate">
<span>
By <b class="author">{{ singlePost?.author }}</b>
{{ singlePost?.date && formatDate(singlePost?.date) }}
</span>
</div>
<div v-if="showContent" class="description_block">
<div class="pre" v-html="singlePost?.body"></div>
<div class="share_block_container">
<UiElementsShareElement />
<UiAddSocialMediaList :is-inline-block="true" :enable-bg="true" color-icon="white" />
</div>
</div>
</div>
</template>
I remove all comment tag in every section and components and try to remove v-if where I could but the error didn't disappeared. The tag ClientOnly don't fit me , because I want that my data render on server.
Upvotes: 2
Views: 6554
Reputation: 11
In my case I've used v-html with variable, which contained a paragraph inside. For example, myVHtmlVariable="<p>sometext</p>"
. And then I inserted it into the paragraph like this: <p v-html="myVHtmlVariable" />
.
I replaced the paragraph with a div: <div v-html="myVHtmlVariable" />
, and this fixed the error.
Upvotes: 1
Reputation: 53
Thanks for your answer Rodrigo. But it isn't the one way to avoid this warning. I've noticed this after creating a few project on Nuxt 3. I catched this warning when I used a table tag and reactive variables inside of the tables but when I rewrote same logic into a div tags the warning disappeared . Also when I forgot the rule that the template must have the only one node and used wrong v-if logic to display blocks like that :
<template>
<div v-if="someVar === "something">
<p>some text in here</p>
</div>
<div v-if="someVar === "otherSomething">
<p>some text in here</p>
</div>
<div v-else>
<p>some text in here</p>
</div>
</template>
So the code above is also leads to the warning. Instead of it, it's necessary to use v-if, v-else-if, v-else structure that the only one block was accessed during the render. And also wrong logic using v-if inside of a one node like that :
<template>
<div v-if="someVar">
<div v-if="someVar === "something">
<p>some text in here</p>
</div>
<div v-if="someVar === "otherSomething">
<p>some text in here</p>
</div>
<div v-else>
<p>some text in here</p>
</div>
</div>
</template>
So in this case to avoid the warning it needs to use the right logic v-if, v-else-if, v-else.
Upvotes: 0
Reputation: 1760
For anyone facing the issue where you're required to use html tags within your component e.g. p, href etc.. tags. Use "ClientOnly" to wrap those elements.
For example -
<ClientOnly>
<div>
<p>some text in here</p>
<a href="<my-link>">Click Me</a>
</div>
</ClientOnly>
Upvotes: 2