Subscribe on changes!

Select box with v-model updates lazy when mounting the component

avatar
Dec 21st 2022

Vue version

3.2.45

Link to minimal reproduction

https://sfc.vuejs.org/#__SSR__eNqtVU1v2zAM/SuEL0mB2m7Xj4PnBOt62mlAO/Tki2PTiTpbEiQ5aVDkv4+SYtdpkyAdepPEx0fySaRegzspo2WLQRKkBhtZ5wanGQdIS7aEos61nmRBIbjJGUeVBc5I5sXldBk2osQaDGoDK2YW8NQiXMHj40Mak32LHBIxitFzkE1OH7HGwszEi2fYcqaxfANpB+lMxOIPsHzK6xYfsBowElxIwwSHpTUS+JKsd2nsT4/gvhHu5wm4K8Ldf8ClsU+qKzqmqv9HANH2he7T4EexyPncpjHGM5hMYbyjBUwAI5OrOZrI5Xt2VJrrLICkI7DJaZ8MluPR9cj6niLczRGWG8dyiqy3R1huHctXiv6Ly9bsCMzsyeCNub0TlRKLPx/hfoHFX7rVPUHMWtqKiy2C+JmteLB/S8MdYjnIgWjqfEZtVwm14zUI6QCfz/khL5mAWWuM4IfzVhbVJS241ae7xN9+12cv2fHknfeUvHZTPhrRrOyyi/jH706O6Lyn5HVIpG6VxoNxmOpCMWlAo2kl1NSClko79VgjhTLwCgor2EClRAMjmqij7xnPOE1OGo7vBxb1KaHHo6vRGaH2YDoAvfwe8PYiO6sdvAPA9q1srUZR+/c2r0vnSIoPiftWI/vYzw07W3Yy8vMEJpOJ1568qf2cLE4gs66tUlH/VcCrlbNkmkRcJ1DV+EI+4BZhyRRRUy8nUIi6bbgzPbfasGodWg7kJgEt8wLDGZoVoofIvCwZn4eKzRcEuLy4kJ62M9RYDc439g6kT6Whucgo4EV3HtkOGNrCmaCn35D7TedNNW4rC84Df9Vhk8voWQtOf6ZzplZyBnoPiaezZ/QE7D4LFsZIncSxrgr70z7rSKh5TKtItdywBiPUTThTYqVREXEWnA84aMSpJapQIS9R2R/4MOc76AdeS0s1bYLNPypOmAY=

Steps to reproduce

  • Open link to minimal reproduction
  • reload page

What is expected?

The select box should show option C directly from the beginning

What is actually happening?

v-model is behaving weirdly when a value is already set while mounting the component.

In the following example the select box initially shows option A then the Component re-renders and shows the correct option C. However the selected ref is set to 3 from the beginning according to the output in the console when I'm logging the selected ref.

<template>
  <div>
    <select v-model="selected">
      <option value="1">A</option>
      <option value="2">B</option>
      <option value="3">C</option>
    </select>
  </div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const selected = ref('3');
</script>

When using the v-bind directive and @change, everything works fine.

<template>
  <div>
    <select @change="(e) => selectedValue = e.target.value">
      <option value="4" :selected="isSelected('4')">A</option>
      <option value="5" :selected="isSelected('5')">B</option>
      <option value="6" :selected="isSelected('6')">C</option>
    </select>
  </div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const selected = ref('6');
const isSelected = (value) => selectedValue.value === value
</script>

Moreover, this error occurs only when using a select box. Other types of input fields (checkbox, radio button, text input) work. Also when doing the same in plain Vue 3 it works.

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.14.2 - /usr/local/bin/node
    Yarn: 1.22.19 - /usr/local/bin/yarn
    npm: 7.17.0 - /usr/local/bin/npm
npmPackages:
    vue: ^3.2.25 => 3.2.45

Any additional comments?

I also created this Nuxt issue related to this topic: https://github.com/nuxt/nuxt.js/issues/15631

avatar
Dec 29th 2022

The same happens with a checkbox. Value is set after control is added to the dom which triggers transition animations.

avatar
Jan 3rd 2023

@edikdeisling Could you provide a minimal reproduction?

avatar
Jan 5th 2023

@edikdeisling this is because scrollIntoView() triggers forces layout/reflow and layout/reflow will restart a CSS animation. see https://www.harrytheo.com/blog/2021/02/restart-a-css-animation-with-javascript/

avatar
Jan 5th 2023

@edison1105 thank you for the info, extremely interesting! But does it mean that vue renders input without taking into account the initial value of v-model? image https://sfc.vuejs.org/#eNqVUk1v2zAM/SuELnGB2Ma+Lq5jdIcddu6wky+OTTdarA9ItNMi8H77KCtZ0i4YtoMhi3x8fHrkUXy2NptGFIUofeukJfBIo61qDSCVNY7gCA77NbRG2ZGwgxl6ZxSsuGwVYOFrjfYEynQ4wCbgE3Ij3t1fctNj68wQskfGjZqZCkhwuINNBceAi0gzYDaYJ85kGp/py4AKNT3K7SD1UyR8DVxtsTcOgeUz/2oNNyuzdoftHrszA4NiwVdN5rvEQ3KTu+kJ3X9Ss4HfpEIzUpJcv+4mNcl2/6/Ec/yZYeazzOO8eFJ8IVR2aAiXuZWdnGBKo+qqzPla1bRkpOYZAr1Y3NRiod+a51owepkdB5eTI/lvqsrx0GUPJzVwkLQDco32kqRhIWf+Mr+SwVdPL0NUFAQtLtR0KSzYj8E4eOeBX4uNu4+7NIdi3r4gtTg3/XnhCD5yXcFr1kVDFjeWZmIt4tamqrHZD280b3bsfEr4WhRnnlrwDod7LXZE1hd5Pmq7Z+ONyh84lzteVJ5l2hn18CF7n338xK/1dB3P0Kt068zBo+OOtVhfkeccnNClDnWHDt1fm73Bvmr4JvdH05MPs5h/Ad3AT88=

It means that mounted hook is called before input gets its value from v-model. Is it an issue? It means that vue inserts input into DOM with invalid state. Also feels a little unexpected that input already exists in the DOM during mounted hook of the previous element. But this can be expected if all component template inserted into DOM at once From my perspective v-model should set value during beforeMount hook before the element is inserted into DOM

avatar
Jan 9th 2023

From my perspective v-model should set value during beforeMount hook before the element is inserted into DOM

It's probably something that can be optimized