Hydration producting mashed up DOM after client side-change of array of dynamic components+data
Vue version
3.3.4
Link to minimal reproduction
https://stackblitz.com/edit/github-vxyebj-xwfogt?file=src%2FApp.vue
Steps to reproduce
consider this App.vue
:
<script setup>
import { ref } from 'vue';
import Blue from './components/Blue.vue';
import Red from './components/Red.vue';
import Black from './components/Black.vue';
const content = ref([
{
key: 'server_1',
component: Blue,
text: 'i am blue',
},
{
key: 'server_2',
component: Red,
text: 'i am red',
},
{
key: 'server_3',
component: Black,
text: 'i am black',
},
]);
if (typeof window !== 'undefined') {
content.value = [
{
key: 'client_1',
component: Blue,
text: 'i am blue',
},
{
key: 'client_2',
component: Black,
text: 'i am black',
},
];
}
</script>
<template>
<div>
<component
v-for="item in content"
:key="item.key"
:is="item.component"
:text="item.text"
/>
</div>
</template>
here, the content
ref gets updated on the client with the middle (red) component missing).
What is expected?
I would expect to see the text i am blue
wrapped in the Blue
component (and thus written in blue color) and i am black
wrapped in the Black
component (and thus written in black color).
What is actually happening?
Instead i am black
is displayed in red, seemingly wrapped in the Red
component, which was removed. the text of any items after the removed component gets shoved into the previous compon
System Info
System:
OS: Linux 5.0 undefined
CPU: (8) x64 Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz
Memory: 0 Bytes / 0 Bytes
Shell: 1.0 - /bin/jsh
Binaries:
Node: 16.20.0 - /usr/local/bin/node
Yarn: 1.22.19 - /usr/local/bin/yarn
npm: 9.4.2 - /usr/local/bin/npm
pnpm: 8.6.10 - /usr/local/bin/pnpm
npmPackages:
vue: ^3.3.4 => 3.3.4
Any additional comments?
No response
You should not use the index as the key, as this can lead to rendering errors when updating the array. You can use the following approach instead:
<component
v-for="item in content"
:key="item.text"
:is="item.component"
:text="item.text"
/>
Of course, for this you need to make sure the key is unique.
@Alfred-Skyblue thanks for your suggestion! unfortunately it does not solve the problem. i've updated reproduction and issue-text using unique keys. the problem still persists.
The root cause is the list is changed during hydration, If the change only happens on the client side, here is a workaround:
onMounted(()=>{
content.value = [
{
key: 'client_1',
component: Blue,
text: 'i am blue',
},
{
key: 'client_2',
component: Black,
text: 'i am black',
},
];
})
@edison1105 Yes, thanks. that's working.
Question is, if my example still is a bug, or if vue principally requires client-side changes to be performed within onMounted()
. If the last one is the case, feel free to close this issue.