Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TransitionGroup fails to hydrate when child has v-if #6715

Closed
jonaskuske opened this issue Sep 21, 2022 · 12 comments · Fixed by #6732
Closed

TransitionGroup fails to hydrate when child has v-if #6715

jonaskuske opened this issue Sep 21, 2022 · 12 comments · Fixed by #6732
Labels
🔨 p3-minor-bug Priority 3: this fixes a bug, but is an edge case that only affects very specific usage. scope: ssr scope: transition

Comments

@jonaskuske
Copy link
Contributor

jonaskuske commented Sep 21, 2022

Vue version

3.2.39

Link to minimal reproduction

https://sfc.vuejs.org/#__SSR__eNp9j81uwyAQhF8F7TmAmpxq0Sg99QV65ELiTUNjYLVgV1Xkdy9urCg/Um4wM7vf7AneidTQIzRgCgbqXMG1jUKYT3Yx++JT/ODU01ls/SCO+PtmwVkQg/T7+ty7LqOF9e7gu1a8GF1T5zjN4e3FXRo9r9KPAKMvFWABPlDiIoMj9Z1TrA1P06CdjWyhEf/KpNUTpr+FQymUG637SMcvtUtBb6qnuY/FB5RtCpuVWqrVa22Zy7WuMAe55fSTkSvRwuJqua7igCwZY4uM/BR2l70B3nkP0Ik52jjC+AeJqoxU

Steps to reproduce

No steps necessary, check the rendered output and the warning

What is expected?

The children inside <TransitionGroup> are rendered just once and the hydration succeeds

What is actually happening?

The child in <TransitionGroup> is rendered twice and a hydration warning appears

System Info

No response

Any additional comments?

I suppose that happens because <TransitionGroup> skips comment nodes (they can't be transitioned and have no boundingClientRect), so it expects the actual child but finds a comment node, "fixes" that by adding the expected child node even though it's already there, just one position behind

@zhangzhonghe
Copy link
Member

I found that this is because the SSR side allows comment nodes in the TransitionGroup, while on the client side it skips the rendering of comment nodes, so it causes a mismatch warning during hydration.

Here is the source code snippet that skips the rendering of comment nodes:

// comment placeholders should be skipped, e.g. v-if
else if (keepComment || child.type !== Comment) {
ret.push(key != null ? cloneVNode(child, { key }) : child)
}

@jonaskuske
Copy link
Contributor Author

jonaskuske commented Sep 22, 2022

Yup! Looking into it atm.

I think one approach would be to pass true for the keepComment arg, another to change the generated SSR code so it doesn't output the comment HTML anymore (_push('<!---->')).

I suspect the first option causes issues because then the comment node is passed to setTransitionHooks(), but maybe the second works? Otherwise the TransitionGroup might need some additional checks, let's see.

@LiamMartens
Copy link

Is there any workaround available for this issue?

@jonaskuske
Copy link
Contributor Author

@LiamMartens

Is there any workaround available for this issue?

Replacing the v-if with a v-for should work:

<!-- error -->
<p v-if="shouldShow">Hello!</p>
<!-- works -->
<p v-for="_ in shouldShow ? [1] : []">Hello!</p>

@barksploit
Copy link

barksploit commented Mar 23, 2023

I've just run into the same issue using Nuxt.

The workaround I found was simply providing a v-else condition to render a self-closing empty element in case the v-if condition returns falsy which seems to solve the comment problem

<p v-if="computedProperty">{{ computedProperty }}</p>
<div v-else />

@haoqunjiang haoqunjiang added the 🔨 p3-minor-bug Priority 3: this fixes a bug, but is an edge case that only affects very specific usage. label Apr 6, 2023
@MehranTaheri
Copy link

MehranTaheri commented Jul 25, 2023

This works for me :

<component :is="component" tag="div">
....
....
</component>
const mounted = ref(false);
const component = computed(() => (mounted.value ? TransitionGroup : 'div'));
onMounted(() => {
    mounted.value = true;
});

@jd-solanki
Copy link

It's been a year, Can we consider this now? @sodatea

@kyng-cytro
Copy link

This worked for me too, had to make the element hidden so it doesn't mess with my styling,

<div class="hidden" v-else />

@SebbeJohansson
Copy link

SebbeJohansson commented Sep 20, 2023

I think this is also happening when using slots that are empty. On the server nothing is rendered, but on the client and empty comment is rendered.
edit: this seems to only happen in nuxt, and not in vue with vite. I will open an issue there.

@detheuss
Copy link

Hey, I just experienced the same issue.
I only had a few child elements in the TransitionGroup, so I decided to use v-show directive instead of v-if.
This seems to have resolved the HM issue in my case and kept the intended animation.
Hope this helps!

jonaskuske added a commit to jonaskuske/core that referenced this issue Mar 1, 2024
@jonaskuske
Copy link
Contributor Author

For me it not only happens with v-if child, but also when you have a slot in a reusable transition component (as mentioned in this closed issue #6922):

sfc.vuejs.org/#__SSR__eNp9kctqwzAQRX9FaJNNLEFbKBg1pHTRH+hSGz+mjRrrwUh2KcH/3pGTJk4M2c3cOeiO7hz4awhi6IGXXMUGTUgsQurDRjtjg8fE3rwN7BO9ZSshc5PxlXZKHnkiqUlgQ1cloI4xlbGporo1A9vD74vmleabZme6llVKkrwk6jNRXwg1uVKp5NmFr/lxvcJWQXxH7+gDh0zr0yBqXrJJyRqtnHvNdymFWErZu7D/Eo23ckszib1LxkLRert9FA/i6ZnsY5rrAqItavQ/EZAcNV/PHpckDoAFgmsBAe+a3bBXhjezhWn2HLUbKYD/W+TTXcf/gZWLJhnv3tFPt5xyjp1PTJ4yXTLzeMc/Dtm2iA==

This doesn't reproduce (anymore?). But v-if causes hydration issues even in the newest release of Vue. I've rebased my PR to get rid of the conflicts

@github-actions github-actions bot locked and limited conversation to collaborators Apr 30, 2024
lynxlangya pushed a commit to lynxlangya/core that referenced this issue May 30, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
🔨 p3-minor-bug Priority 3: this fixes a bug, but is an edge case that only affects very specific usage. scope: ssr scope: transition
Projects
None yet
Development

Successfully merging a pull request may close this issue.