vue icon indicating copy to clipboard operation
vue copied to clipboard

Node attribute value is not reactive after client side hydration

Open berniegp opened this issue 4 years ago • 10 comments

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 the div
  • 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.

berniegp avatar May 18 '20 08:05 berniegp

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

posva avatar May 18 '20 09:05 posva

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.

cinsanity avatar May 18 '20 09:05 cinsanity

@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.

berniegp avatar May 18 '20 10:05 berniegp

@cinsanity I'm not sure I understand. I attached a screenshot to give more details.

chrome_2020-05-18_12-34-22

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.

berniegp avatar May 18 '20 10:05 berniegp

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 avatar May 18 '20 10:05 posva

@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.

berniegp avatar May 18 '20 10:05 berniegp

I am very curious about why $forceUpdate() doesn't update the div's id though.

berniegp avatar May 18 '20 10:05 berniegp

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.

itchwoot avatar Apr 28 '21 14:04 itchwoot

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

valdoryu avatar Feb 16 '22 13:02 valdoryu

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>

wobsoriano avatar Dec 24 '22 06:12 wobsoriano