vue
vue copied to clipboard
Node attribute value is not reactive after client side hydration
Version
2.6.11
Reproduction link
https://codesandbox.io/s/ssr-reactivity-up6nh?file=/pages/index.vue
Steps to reproduce
- The id attribute of the
div
does not match the myid value from the component (requires page refresh sometimes on first load). - Notice that
$forceUpdate()
(first button) does not update the id attribute of thediv
- Changing the value of the
val
data (second button) property triggers the reactivity
What is expected?
Every reference to the myid
computed property should match
What is actually happening?
The id attribute of the div
does not match the myid
value
The same component used in client-side rendering does not exhibit this issue.
I don't think it's possible to use random values with hydration like that because the html rendered by the server should be the same as the one generated by the client. By using random values, you are pretty much forcing an hydration mismatch because they will (almost) always differ. But I don't understand why there is no error
The value of val in data is a certain value after initialization, so when run $forceUpdate() will not run Math.floor(Math.random() * 10000000), the value of val is not changed.
@posva Perhaps I shouldn't have used Math.random()
. The point is that if val == X
on the server side and val == Y
on the client-side after hydration, most parts of the component are updated reactively while the id
attribute of the div
is not. I can modify the example to use something else than random values if it helps, but it doesn't materially change the (alleged) bug I'm describing.
@cinsanity I'm not sure I understand. I attached a screenshot to give more details.
I expected $forceUpdate()
to make all the id values match when forcing a re-render, but it does not. Only changing val
successfully synchronizes all the ids.
The point is that if val == X on the server side and val == Y on the client-side after hydration
It has to really be after hydration. In that case it might be related to one of these linked issues https://github.com/vuejs/vue/issues/10260 (see linked issues as well)
@posva yeah it looks similar to #10260, but I'm not knowledgeable enough about Vue internals to be sure.
I did update my repro to use Vue.prototype.$isServer
instead of Math.random()
for greater clarity.
I am very curious about why $forceUpdate()
doesn't update the div
's id
though.
We ran over the exact same issue with Nuxt when creating a uid in the created hook, our workaround was creating a new ID in the mounted hook.
Hi, I encountered the same issue Is there any other solution than having to use mounted ? We generate unique IDs for our components and use them also for some aria attributes
If we set those ID's in mounted, we will loose SEO gain from accessibility attributes
Edit: putting a ref on the bugging node solve the issue
For Vue 3, there's a built-in getSSRProps hook available via directives:
<script setup lang="ts">
import type { Directive } from 'vue'
import { nanoid } from 'nanoid';
const vUniqueIds: Directive = {
created(el, binding) {
el.setAttribute('id', binding.value.id)
el.setAttribute('data-whatever', binding.value['data-whatever'])
},
getSSRProps(binding) {
return {
id: binding.value.id,
'data-whatever': binding.value['data-whatever']
}
},
}
</script>
<template>
<div>
<div v-unique-ids="{ id: nanoid(), 'data-whatever': nanoid() }">
hello
</div>
</div>
</template>