Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

defineModel assigned a value cannot be read #12787

Open
matthew-dean opened this issue Jan 29, 2025 · 4 comments
Open

defineModel assigned a value cannot be read #12787

matthew-dean opened this issue Jan 29, 2025 · 4 comments

Comments

@matthew-dean
Copy link

matthew-dean commented Jan 29, 2025

Vue version

3.5.13

Link to minimal reproduction

https://play.vuejs.org/#eNqVUl1L7DAQ/Stz89IVvC2y974s9cJVV1DwA/UxILWdrtE0LUlaF0r/u5O0m9X1A3xL5pw5OSczPfvfNHHXIluw1ORaNBYM2rb5x5Womlpb6EFjCQOUuq4gImoUoOO6aqZ6nLiLUyI4r5UhHZtpe5JZhEMnMdvjKk3GN0idLharRhJON4DUi3W/q7pAuQi9h5yFM2eQEDdN3jSyfWYNvVeKVfxkakU5eifHWU56QqK+aqwgP5wtwCMOy6SsX859zeoW9zf1/BHz50/qT2btapxdazSoO7ISMHK3QjvCy9tLXNM5gJSmlcT+BrxBU8vWeRxpR60qyPYbnnd75r9cqNWdWa4tKrMJ5Yw65uD5nNEI3Fd+FX1rdx7/8X1cDfSLm/H9bBE+TrrAUii8cFOcRQGIaPgjF1XxGXMqOx5XEifN7ea4EnG2BVHC7FfQj7tMtrg3xtypUk90nik4IL+weX8LneLDBPm+AOyoTL0BfqdDX/j9chei8weAW59LGOj7KeMwpA96QpcUccRc2IFUqTkZu3f3/r5D7baAJjaP/8YHcza8AtXQR/o=

Steps to reproduce

This baffled me for the last few hours. If a defineModel is assigned a value, that value cannot subsequently be read. This behavior differs from refs, which can be read as soon as they're written, and my basic understanding is that defineModel defines a type of ref? It also seems to only happen when an undefined value is assigned to the model, which then I guess gets updated and somehow interrupts value retrieval for the model? 🤷‍♂

What is expected?

Look at this component:

<script setup>
import { ref } from 'vue'
const startDate = defineModel('startDate')
const endDate = defineModel('endDate')

let start = ref()
let end = ref()
if (!startDate.value) {
  startDate.value = 'Jan 1'
  endDate.value = 'Feb 1'
  start.value = startDate.value
  end.value = endDate.value
}
</script>

<template>
  <div>
    Start is {{ start }}<br>
    End is {{ end }}
  </div>
</template>

Ask any developer what will be output. If they say the output will be this, they are wrong:

Start is Jan 1
End is Feb 1

The output is actually:

Start is
End is Feb 1

What is actually happening?

defineModel has wildly different and unpredictable behavior based on whether or not refs are assigned.

System Info

System:
    OS: macOS 15.1.1
    CPU: (10) arm64 Apple M1 Pro
    Memory: 89.83 MB / 16.00 GB
    Shell: 5.9 - /bin/zsh
  Binaries:
    Node: 20.12.1 - ~/Library/Caches/fnm_multishells/78455_1738117758436/bin/node
    Yarn: 1.22.19 - ~/Library/Caches/fnm_multishells/66567_1738082123979/bin/yarn
    npm: 10.5.0 - ~/Library/Caches/fnm_multishells/78455_1738117758436/bin/npm
    pnpm: 8.10.2 - /opt/homebrew/bin/pnpm
  Browsers:
    Chrome: 132.0.6834.111
    Safari: 18.1.1
  npmPackages:
    vue: ~3.5.12 => 3.5.13

Any additional comments?

By the way, the reason I'm doing it this way vs a default value in defineModel is that the value is set by other types of reactive values. This is just a simple repro.

@jacekkarczmarczyk
Copy link
Contributor

https://vue-land.github.io/faq/emit-synchronous
#11832 (and probably others)

@matthew-dean
Copy link
Author

@jacekkarczmarczyk

So, I just read that thread and the fact it was listed as "expected behavior" is BONKERS.

I updated my example above with the following example:

import { ref } from 'vue'
const startDate = defineModel('startDate')
const endDate = defineModel('endDate')

let start = ref()
let end = ref()
if (!startDate.value) {
  startDate.value = 'Jan 1'
  endDate.value = 'Feb 1'
  start.value = startDate.value
  end.value = endDate.value
}

How does it make any reasonable sense, that based on this code, end will have a value of 'Feb 1', and start will have a value of undefined? In what universe is that good, sensible DX?

@absidue
Copy link

absidue commented Jan 29, 2025

It only seems bonkers if you think of it as a ref and expect it to act like a ref instead of as what it actually is, reading the value from a prop and emitting events when you write to the model. If it is too complicated to remember how defineModel works, it might be easier for you to stick to manually adding props and emits to your code. As for why end gets and value but start doesn't, endDate doesn't have a v-model or a listener in the parent component so it acts like a normal ref (the alternative would be for it to just do nothing/not work if nothing is added in the paren, which would be worse), whereas startDate does so it gets the proper emits and props treatment.

@alextana
Copy link

alextana commented Jan 29, 2025

@jacekkarczmarczyk

So, I just read that thread and the fact it was listed as "expected behavior" is BONKERS.

I updated my example above with the following example:

import { ref } from 'vue'
const startDate = defineModel('startDate')
const endDate = defineModel('endDate')

let start = ref()
let end = ref()
if (!startDate.value) {
startDate.value = 'Jan 1'
endDate.value = 'Feb 1'
start.value = startDate.value
end.value = endDate.value
}
How does it make any reasonable sense, that based on this code, end will have a value of 'Feb 1', and start will have a value of undefined? In what universe is that good, sensible DX?

@matthew-dean if you directly assign the model to start.value then it works as you expect it

<script setup>
import { ref } from 'vue'
const startDate = defineModel('startDate')
const endDate = defineModel('endDate')

let start = ref()
let end = ref()


if (!startDate.value) {
  startDate.value = 'Jan 1'
  endDate.value = 'Feb 1'

  start.value = startDate <-- instead of accessing .value you can directly assign it to the model
  end.value = endDate <-- same here
}

</script>

<template>
  <div>
    Start is {{ start }}<br>
    End is {{ end }}
  </div>
</template>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants