Subscribe on changes!

Components with same key remount after v-if toggle

avatar
May 31st 2023

Vue version

3.3.4

Link to minimal reproduction

https://play.vuejs.org/#eNqNUj1z2zAM/SsIF9l3pjikk6qk7tahd+3QUYsigTJrfh1JuZfT6b8XouJWTntptYh4AN8D+DCxj96XlxFZxerYBeUTREyjB93a4aFhKTbssbHKeBcSTBBQwgwyOAMNo3sNe/8r+9mN/TVViiVamHNF52xM4OwXKb+5YdAIDwvVTrY64p4KarGqk1ad0HjdJqQzQP00puQsHDutujN1dEtytwmp0xd8mrZVH6BwUhZQ0d8W81yLlXPlv6rBhSt5y59HB/rqPNoZn3OecKguraagOKHWriBArGzitvkNOdKk/0H3/JuMCu84hx41EkM6qQidMwZtAs7/Jrc84wZgB7Y6w03ry+/RWXJ5yoUvCTK3gowsWDarosMpJR8rIUbrz0NJmuJIORFGm5RB3jtzvC/vy3eiVzFt4RKj4U/B/YgYSLBhhw23IPCCgQe0PQYMb2q9qt3qvUr9oblIzo2dafzrDv5ju5ftdBpL7YZdkb3JVbs9dK3W2Bf7hrSksvg1OB/r/GLkWAUxBWUHmnJ+3L25xp/UgbZyuQQzNbfxic0/AaJ1MjQ=

Steps to reproduce

  1. Click toggle
  2. See that 'Loud setup() called' gets logged to console with every click.
  3. Delete comment
  4. Click toggle
  5. See that 'Loud setup() called' only gets logged once.
  6. See that Loud component doesn't update its dom despite the props value change.

What is expected?

Every toggle, Loud's prop value changes, so I expect that to be reflected in the dom. Further, since Loud's keys match, I expect to only see 'Loud setup() called' once.

What is actually happening?

If the comment is there, it remounts the component every time. If the comment is removed, it never updates the component when props changes.

System Info

System:
    OS: macOS 13.3.1
    CPU: (8) arm64 Apple M1 Pro
    Memory: 256.58 MB / 16.00 GB
    Shell: 5.2.15 - /opt/homebrew/bin/bash
  Binaries:
    Node: 18.14.2 - ~/opt/node-v18.14.2-darwin-arm64/bin/node
    npm: 9.5.1 - ~/opt/node-v18.14.2-darwin-arm64/bin/npm
  Browsers:
    Chrome: 113.0.5672.126
    Safari: 16.4

Any additional comments?

I have a component with a video in the dom that I would like to just move around via some layout config.

<template v-if="useLayout1">
  <MyCompWithVideo key="one" ... />
  <MyCompWithVideo key="two" ... />
</template>
<template v-else>
  <MyCompWithVideo key="two" ... />
  <MyCompWithVideo key="one" ... />
</template>
avatar
May 31st 2023

This is expected behavior in Vue 3 (which is intentionally different from Vue 2). v-if branches always remounts because they are considered branches with different keys at the root. Keys on nested elements won't affect the behavior.

avatar
Jun 1st 2023

So is it bug if they are not unmounting when the comment is removed then?

<script setup>
const toggle = ref(true)
...
</script>
...
<template v-if="toggle">
  <Loud key="one" val="hi" />
</template
<template v-else>
  <Loud key="one" val="hey" />
</template>

It renders hi, but on toggle, never renders hey.

avatar
Jun 1st 2023

Vue tries to reuse nodes with the same key and tag under the same parent element as much as possible. However, since the template is not a real node, your v-if and v-else are under the same parent element. You can use different keys to render them or use different parent elements to render them.

<template v-if="onOffToggle">
    <Loud key="one" :val="'hello'" />
</template>
<template v-else>
    <Loud key="tow" :val="'hey'" />
</template>

or

<div v-if="onOffToggle">
    <Loud key="one" :val="'hello'" />
</div>
<div v-else>
    <Loud key="one" :val="'hey'" />
</div>
avatar
Jun 1st 2023

Ok, cool. Thanks for taking time to respond. I see how this is undefined behavior territory now