Nuxt3 + Prismic + i18n : Changing block ordering

I reached out for some help and the issue you're facing is likely due to the asynchronous data fetching and state management in your Nuxt 3 project when changing languages via the language flag (without refreshing the page). When you switch languages, the component re-renders and the data fetching may not be synchronized properly, leading to the display order changing unexpectedly.

Here are a few steps to troubleshoot and fix this issue:

1. Ensure Proper Reactivity with useAsyncData

Nuxt's useAsyncData is designed to handle server-side rendering (SSR) and client-side data fetching, but it might not always react to changes in a reactive variable like locale. When you switch languages, you need to make sure that the data is refetched and the component properly reacts to the change.

Solution: Use watch to refetch data whenever the locale changes.

Update your page file script to use watch for locale:

<script setup lang="ts">
import { components } from "~/slices";
import { ref, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRuntimeConfig, useAsyncData, usePrismic } from 'nuxt-composition-api';

const { locale } = useI18n();
const config = useRuntimeConfig();
const prismic = usePrismic();
const page = ref(null);

const fetchData = async () => {
  const { data } = await prismic.client.getSingle("pagecarnetsnegociation", {
    lang: locale.value === "en" ? "en-gb" : "fr-fr",
  });
  page.value = data;
};

await fetchData();

watch(locale, async () => {
  await fetchData();
});
</script>

<template>
  <SliceZone wrapper="main" :slices="page?.data?.slices ?? []" :components="components" />
</template>

2. Ensure Data Fetching Logic for Slices is Reactive

In your slice file, the use of useLazyAsyncData with await could be causing issues, especially since this should be reactive to changes in locale. If locale changes, the slice component should refetch the data accordingly.

Solution: Make sure the slice also refetches data on language change.

Update the slice script as follows:

<script setup lang="ts">
import * as prismic from "@prismicio/client";
import { ref, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import { usePrismic } from 'nuxt-composition-api';

const { client } = usePrismic();
const { locale } = useI18n();

defineProps(
  getSliceComponentProps<Content.CarnetsNegociationSlice>(["slice", "index", "slices", "context"]),
);

const carnets = ref(null);
const status = ref('idle');
const error = ref(null);

const fetchData = async () => {
  status.value = 'pending';
  try {
    const response = await client.getByType("carnetnegociation", {
      lang: locale.value === "en" ? "en-gb" : "fr-fr",
      orderings: {
        field: "my.carnetnegociation.date",
        direction: "desc",
      },
    });
    carnets.value = response;
    status.value = 'success';
  } catch (e) {
    error.value = e;
    status.value = 'error';
  }
};

await fetchData();

watch(locale, async () => {
  await fetchData();
});
</script>

<template>
  <v-container id="containerCarnetsNegociation">
    <div v-if="error">{{ error }}</div>
    <div v-else-if="status == 'pending'" class="text-center">
      <v-progress-circular indeterminate color="primary"></v-progress-circular>
    </div>
    <div v-else-if="carnets && carnets.results.length > 0">
      <div class="d-flex flex-wrap justify-center">
        <v-card
          v-for="carnet in carnets.results"
          :key="carnet.id"
          width="340px"
          flat
          rounded="0"
          :to="localePath(`/carnet-negociation/${carnet?.uid}`)"
          class="ma-2 ma-md-4 ma-lg-6 ma-xl-8"
        >
          <div class="headerCard px-4 py-10">
            <h3>{{ carnet.data.titre }}</h3>
            <div class="mt-5 ouvrir">{{ slice.primary.ouvrir_le_carnet }}</div>
          </div>
          <v-img :src="carnet.data.photo.url" width="100%" height="250px" cover></v-img>
        </v-card>
      </div>
    </div>
  </v-container>
</template>

3. Ensure Keyed Re-rendering in Vue Components

Vue relies on keyed components to understand when elements are updated. Make sure all loops, such as v-for loops, have a unique key attribute.

Solution: Ensure all v-for directives have a key:

<v-card
  v-for="carnet in carnets.results"
  :key="carnet.id"
  ...
>

4. Check if State is Properly Managed

Ensure that your state management is consistent and that no reactive data is being mutated incorrectly. In Vue, direct mutation of props or other reactive sources can lead to unexpected behaviors.

Summary

The key to solving your issue is ensuring reactivity and proper data fetching logic when switching locales. Use Vue's watch to observe locale changes and refetch data as needed, and ensure that all components are correctly keyed to prevent Vue from reusing DOM nodes inappropriately.