nuxt / icon

The <Icon> component, supporting Iconify, Emojis and custom components.
https://stackblitz.com/edit/nuxt-icon-playground?file=app.vue
MIT License
929 stars 51 forks source link

SSR Body Teleport causes hydration children mismatch #37

Open Kasparas-G55 opened 1 year ago

Kasparas-G55 commented 1 year ago

Repro: StackBlitz

root5427 commented 1 year ago

Did you happen to solve this problem by chance?

Kasparas-G55 commented 1 year ago

Did you happen to solve this problem by chance?

Rendering the Icon in client only is the only band-aid solution I know currently. Might look into it deeper when I have free time.

Kasparas-G55 commented 1 year ago

Seems like this is an issue with ssrRenderTeleport and resolveTeleports function in Vue when setting targetBuffer (__teleportBuffers[target]) with splice, instead of it being set to a SSRBuffer, it becomes an [SSRBuffer] that doesn't contain hasAsync and ends up unrolling the buffer synchronously when it contains pending promises.

resolveTeleports calls unrollBuffer with awaited Promise.all parameter which also returns [SSRBuffer] without the hasAsync property and ends up unrolling the buffer synchronously.

@danielroe could you have a look if I'm correct in my assumption? 🙏

I've fixed it by setting hasAsync after slicing targetBuffer

targetBuffer.splice(bufferIndex, 0, teleportContent);
targetBuffer.hasAsync = typeof teleportContent !== 'string' && teleportContent.hasAsync

And removed usage of Promise.all in resolveTeleports (seems like perf decrease though 🤔)

for (const key in context.__teleportBuffers) {
  context.teleports[key] = await unrollBuffer(context.__teleportBuffers[key]);
}

Not sure if there's a better way to do this. 🤷‍♂️

Kasparas-G55 commented 11 months ago

fixed on https://github.com/vuejs/core/pull/9431