Search code examples
nuxt.jsnuxtjs3piniavee-validateprimevue

Hydration completed but contains mismatches using VeeValidate and Pinia in Nuxt


this week I'm having a weird error into my Nuxt app, it's a small app, it's only a landing page with a register form for an event, I worked on this last week and I published it and it works fine. Now, I have to add some modifications into the register form, nothing complex, but when I ran the project locally, without update anything, and I'm getting now a hydration error into the fields 😢. Again, it's too weird for me because I don't update or change anything, Im running the exactly code version that is published, and if I run the build and preview cmd, I'm having the same error.

Now, the code:

Into the form I'm using this stack:

  • PrimeVue InputText
  • VeeValidate with Zod
  • Pinia for store the data because the form has different steps.

In my component I do this:

const registerFormStore = useRegisterFormStore();


const validationSchema = toTypedSchema(
  object({
    [DocumentFormFields.DOCUMENT_ID]: z
      .string({ required_error: 'Ingresa tu documento de identificación' })
      .min(6, 'error message')
      .max(15, 'error message')
      .transform((value) => value.trim().toLocaleUpperCase())
      .default(registerFormStore.personalInfo.documentId || '')
  })
);
const { errors, handleSubmit, defineField, setErrors } = useForm({
  validationSchema
});

const formValues = reactive({
  documentId: defineField<DocumentFormFields.DOCUMENT_ID, string>(DocumentFormFields.DOCUMENT_ID)[0]
});
<form class="flex flex-col gap-5" @submit.prevent="onSubmitForm">
      <div class="flex flex-col gap-2">
        <label for="documentId" class="font-medium">Document Label</label>
        <IconField icon-position="left">
          <InputIcon class="pi pi-id-card" />
          <InputText
            id="documentId"
            v-model="formValues.documentId"
            placeholder="Placeholder text"
            class="w-full"
            maxlength="15"
            :disabled="isLoading"
            :invalid="Boolean(errors[DocumentFormFields.DOCUMENT_ID])"
          />
        </IconField>
  
        <transition name="fade">
          <small v-if="errors[DocumentFormFields.DOCUMENT_ID]" class="text-sm text-danger">
            {{ errors[DocumentFormFields.DOCUMENT_ID] }}
          </small>
        </transition>
      </div>
    </form>

and now I'm having the hydration error:

enter image description here

[Vue warn]: Hydration attribute mismatch on <input class=​"p-inputtext p-component w-full" id=​"documentId" placeholder=​"Escribe tu documento de identidad" maxlength=​"15" data-pc-name=​"inputtext" data-pc-section=​"root">​ 
  - rendered on server: (not rendered)
  - expected on client: value=""
  Note: this mismatch is check-only. The DOM will not be rectified in production due to performance overhead.
  You should fix the source of the mismatch. 
  at <InputText id="documentId" modelValue="" onUpdate:modelValue=fn  ... > 
  at <IconField icon-position="left" > 
  at <DocumentValidationForm key=3 > 
  at <RegistrationComponent key=2 > 
  at <FormSection> 
  at <Index onVnodeUnmounted=fn<onVnodeUnmounted> ref=Ref< undefined > > 
  at <Anonymous key="/" vnode= {__v_isVNode: true, __v_skip: true, type: {…}, props: {…}, key: null, …} route= {fullPath: '/', hash: '', query: {…}, name: 'index', path: '/', …}  ... > 
  at <RouterView name=undefined route=undefined > 
  at <NuxtPage> 
  at <App key=3 > 
  at <NuxtRoot>

And later, all the other steps into my form are broken, for example, when I change to the next step, I load the documentId and the username, and the fields now are broken and don't take the default value:

const validationSchema = toTypedSchema(
  object({
    [PersonalInfoFormFields.NAME]: z
      .string({ required_error: 'error text' })
      .min(2, 'error text')
      .max(100, 'error text')
      .transform((value) => value.trim())
      .default(registerFormStore.personalInfo.name || ''),
    [PersonalInfoFormFields.EMAIL]: z
      .string({ required_error: 'error text' })
      .email('error text')
      .transform((value) => value.trim().toLocaleLowerCase())
      .default(registerFormStore.personalInfo.email || '')

The data into the store is OK, but into the fields not

enter image description here

Look that all the fields are undefined when it should be with an empty string

I already work and search about this error but I don't have any idea why it's happening. 🥲 I'm not sure if it's related with Pinia, because the console of store installed

enter image description here

but the weird think is that I do not update any library version or similar.

Any help is welcome!, thanks in advance.


Solution

  • I found that the error was caused for different things:

    • Using Date.now() as initial value in some ref() const, example const myVar = ref(Date.now()); DON'T do that, initialize the ref in zero and later into the onMounted set the value myVar.value = Date.now()

    • I was using refine for validate two form fields:

    const validationSchema = toTypedSchema(
      object({
        [PersonalInfoFormFields.EMAIL]: z
          .string({ required_error: 'error message' })
          .email('error message')
          .transform((value) => value.trim().toLocaleLowerCase())
          .default(registerFormStore.personalInfo.email || ''),
        [PersonalInfoFormFields.REPEAT_EMAIL]: z
          .string({ required_error: 'error message' })
          .email('error message')
          .transform((value) => value.trim().toLocaleLowerCase())
          .default('')
      }).refine(({ email, repeatEmail }) => email === repeatEmail, {
        message: 'error message',
        path: [PersonalInfoFormFields.REPEAT_EMAIL]
      })
    );
    

    If I remove this (the refine function), all works fine, otherwise, I have the error that the fields are in undefined.

    I was able to resolve my error with those steps.

    Special thanks to @kissu that help me to found the errors