Hydration Mismatch when using v-for to render a Collection

Hello, I’m testing my SortableJS custom component, when I use it within the Editor, its works like clockwork, but when I publish the page, it seems like the order of load changes and some sort of Mismatch happens.

updated - means that my collection is fetched and parsed into an array
ID_… - console.logs() the ID of the component wrapper, which then calls the new Sortable(id, …) … class

Editor view (console logs)

Screenshot 2023-08-23 at 11.37.53

Published view (console logs)
The element that is loading too soon, and then mismatching is actually getting data directly from a Supabase collection

The other (working) elements are getting data from a processed array, based on that same collection.

I have the :key on the data that is going into the component, so it might be something related to the component rerendering?

Screenshot 2023-08-23 at 11.38.01

I’m trying to understand the flow of how it works, I tried to load the Collection on Page, App load, or also before fetching, but still seems to be bugging out.

Just to be sure I understand correctly: your collection are loaded in a different order in editor and in publish?

I’m not sure really, it seems like the first IDCKO_4316… element is being loaded differently (before the collection is fetched via a workflow on Published, while it loads nicely in Editor.

Edit: I tried to update the data so it has all the same data for all the components, and it seems like that one component is just loading sooner for some reason, or not working properly. I’m initiating new Sortable instance on mounted() but it seems like there is some sort of mismatch with IDs.

On the second image it seems like one error is because you are not passing an element to sortable when initializing, you should look into how are you getting the element. It’s easier to use a ref to the element instead of querying for an id.

Hey, could you elaborate on the ref? You mean referencing the element via a variable? If you mean ref in Vue, that seems to be null on mount, so that wouldn’t be a good idea

It seems pretty weird to me, because they’re all the same component, just different instances. And only one of them is not loading properly.

The difference maybe because of the prerendering/hydration. Can you try with prerendering disabled?

Mariano’s hint was the right one. It indeed was because of prerendering I thing @aurelie, I needed to generate unique ID’s, I did this with a Math.random and it seems like it did change during the process, so it caused Mismatch with hydration. I still get the error, but upon using refs (thanks @dorilama) reference the ref instead of an ID so the element is always same, even if ID changes.

I still get the hydration error though… The weird thing is that id did it just for the first one, all the other ones were working fine.

TLDR: Don’t use generated (by a function) unique ID’s on custom components, as it changes with prerendering/hydration, and causes mismatches.

1 Like

Sometimes you need a unique id (for example form input).
There are different approaches available

  • Use the uid of the component. Attention that if repeated, element has the same uid
  • Ask the user to provide a path to unique id (or an id) themself
  • Generate a random one, and put it as an attribute (for ex data-my-id). When creating the id, first check if there is already one inside this.$attrs. If exist use it, and do not generate a new one.
1 Like

Yeah, I did the last one but without a check actually. So it just switched IDs I think and then died on me.

Posting here because seems to be the same issue like with the other thing, prerender/hydration is out of sync.
For some reason, when I render the items with v-for like this

<template>
  <div ref="sortableContainer" class="container">
    <template v-if="items.length > 0"> <-- had to add this in order to eliminate the issue
      <div :key="item" v-for="(item, index) in items">
        <wwLayoutItemContext is-repeat :data="item" :item="null" :index="index">
          <wwElement v-bind="content.itemContainer"/>
        </wwLayoutItemContext>
      </div>
    </template>
  </div>
</template>

In prerender after Publish it causes Hydration mismatch and looks like this with the first item being empty.

With the v-if, it at least visually seems to be working.
In editor works fine, but with Prerender, when I use collections, it causes me to have an empty flexbox slot at the start. This one seems like an issue on my side with wrong setup of the logic behind binding the collections.

The <script> looks like this

import Sortable from 'sortablejs';

export default {
  name: "simple",
  display: "Simple",
  props: {
    content: { type: Object, required: true },
  },
  computed: {
    items() {
      return wwLib.wwCollection.getCollectionData(this.content.data) || [];
    },
  },
  emits: ["trigger-event"],
  mounted() {
    if (window) {
      const el = this.$refs.sortableContainer;
      Sortable.create(el, {
        group: "sameGroup",
        onEnd: (evt) => {
          this.$emit("trigger-event", { name: "drag:end", event: evt });
        },
        onMove: (evt) => {
        },
      });
    }
  }
};

I think pretty normal, nothing that could cause this. Seems like it might be caused by items() returning an empty array and that rendering as empty one item, but how to avoid that, other than looking for the length? Do you at WeWeb have any method/way to do this? The template thing is a little ugly imo. I could move it to the div also, but I think that v-if in this case might not be the best practice and if there is a way to avoid this @aurelie, I’d rather learn how :slight_smile:

I’m trying to make this work for everyone so I can share it, that’s why I just don’t turn off prerendering ahaha

Your empty flexbox is probably due to your container no?
What you can do is provide a wwLayout for when the list is empty, so that user can customize what they want to render in this situation

It actually renders the wwElement even if it is empty (with null values in the

tag). Unless I use v-if, but is it a good practice to do it like this? I don’t see any examples of how you handle empty collections in the github components :frowning:

I’m still kinda trying to fight the hydration mismatch as well, because v-if doesnt solve that one.

You will always have the weweb wrapper.
So wwElement will always be render, what you can do is render something different when empty
What you can do is have a v-else to display a special wwLayout so that user can control what an empty list looks like

So it’s not a bug but a feature yes? :smiley: I’d like to find out how you guys handle this. What still bothers me @aurelie is the Hydration error :frowning:

Can you provide me the html in the editor and the html in the publish (before hydration?) so i can see whats happening? (only the relevant part)