Subscribe on changes!

v-model not working on checkbox for nested ref

avatar
Jun 8th 2023

Vue version

vue@3.3.4

Link to minimal reproduction

https://play.vuejs.org/#eNplUUtugzAQvcrIG4hCknZLk0hVj9DuShfUGcCKsS3b0EaIu3cMriDKzvN7Pw/s1Zh93yHL2dFja2Tp8VwogONF9NNjfkK/q7Q9Fcyh90LVIBTEpysY5Fe8LcM9VQWL1wFAKNN5gmj1BeVqry8lHfubQWryBvn1W/9S55/4sIh4i1PgTalqvIAXLbochiE2PkI9jpP2eHc8rCxR6bgVxgfdnQFJV8TqST5NRWu09TCAxSqDn9LzBkaorG6hYJRPwV4CBNfKEQBpl/gekzjBEEjJcw5JdLZ7TrLQJIN5gEyrUjrcUI8EziBr2YQRlp42gWMiT+84QlAZpLyzFpXPwFjshe7cBk7nmT1gaol7qes0idBJBo8X0/JCvd2Sr3Hijd7ip5KkzzsNX1OEhzlDSoyNf6flx1k=

Steps to reproduce

Click the checkbox few times

What is expected?

Counter should be incremented when clicking on the checkbox (also a log in the console should be shown)

What is actually happening?

Counter is not incremented and no log appear in the console

System Info

No response

Any additional comments?

No response

avatar
Jun 8th 2023

From some further testing, it looks like v-model will replace the whole Ref with a primitive boolean value after you press the checkbox. It seems that the root object is assumed to be reactive but it isn't in this case.

I feel like I have an intuition for why this happens, but it's a bit unfortunate footgun, combined with the recommendation to return non-reactive objects from composables (my original intent was to create some useSetting composable here).

avatar
Jun 9th 2023
<template>
  <div>
    <div v-for="setting in settings" :key="setting.key">
       <input v-model="setting.val.value" type="checkbox" >
    </div>
    Checkbox changed times: {{changedTimes}}
  </div>
</template>

<script setup lang="ts">
import { ref, watch } from "vue";
const singleSetting = {
  key: 'setting-1',
  val: ref(false),
}
const changedTimes = ref(0)

watch(singleSetting.val, (current, previous) => {
  console.log('changed', current, previous)
  changedTimes.value++;
})

const settings = [singleSetting]

</script>
avatar
Jun 9th 2023

I don't think it's a bug. In your usage method, in the template, ref will not be unwrapped automatically, you need to access .value by yourself (https://vuejs.org/guide/essentials/reactivity-fundamentals.html#ref-unwrapping-in-templates)

avatar
Jun 9th 2023

In your demo there are typo mistakes and ref syntax errors

const changedTimes = ref(0)

watch(singleSetting.val, (current, previous) => {
  console.log('changed', current, previous)
  changedTime++;
})
avatar
Jun 9th 2023

I don't think it's a bug. In your usage method, in the template, ref will not be unwrapped automatically, you need to access .value by yourself (https://vuejs.org/guide/essentials/reactivity-fundamentals.html#ref-unwrapping-in-templates)

I tried that and it generates an error. But doing it in the reproduction didn't, I'll have to investigate in the original code. Also, this link is dead, the chapter is not present.

In your demo there are typo mistakes and ref syntax errors

const changedTimes = ref(0)

watch(singleSetting.val, (current, previous) => {
  console.log('changed', current, previous)
  changedTime++;
})

oh, sorry. I messed it up when reducing the problem.

avatar
Jun 9th 2023

Looks like the problem was leftower state from hot reload (changing to .value access) tried to set value on raw boolean, that was left from previous version with ref that was assumed to be unwrapped. If I refresh the page, it works correctly (when accessing .value).

Thanks for looking at this.