Vue.js 3 : a `v-model` looses reactivity when `data()` converted to `setup()`

542 views Asked by At

Consider this basic example where we have an input and a p that shows the value of the input:

const App = {
  data() {
    return {
      message: "hello world!"
    };
  }
};


Vue.createApp(App).mount('#root');
<script src="https://unpkg.com/vue@next"></script>
<div id="root">
  <input v-model="message"/>
  {{ message }}
</div>

When you change the input text it is reflected in the content of the p. But if you replace data() with setup(), the changes to input are no longer reflected in p:

const App = {
  setup() {
    return {
      message: "hello world!"
    };
  }
};


Vue.createApp(App).mount('#root');
<script src="https://unpkg.com/vue@next"></script>
<div id="root">
  <input v-model="message"/>
  {{ message }}
</div>

A simple fix is to ref the message:

const App = {
  setup() {
    const message = Vue.ref("hello world!");
    return {
      message
    };
  }
};


Vue.createApp(App).mount('#root');
<script src="https://unpkg.com/vue@next"></script>
<div id="root">
  <input v-model="message"/>
  {{ message }}
</div>

But why we have to do it? Why it doesn't work out of the box?

I thought it might be because the object returned from data() is made reactive internally but the object returned from setup() doesn't because that object might contain not only data but also methods that don't need to be observed but when I check inputEl.__vueParentComponent.setupState I see that it's a Proxy. So, why it doesn't work?

1

There are 1 answers

0
Boussadjra Brahim On

return {...} just exposes that values to the outside of setup function, you should always use ref or reactive to make your data reactive :

setup() {
    const message = Vue.ref("hello world!");
    return {
      message
    };
  }

You could see the comment here that says // expose to template :

<template>
  <div>{{ count }} {{ object.foo }}</div>
</template>

<script>
  import { ref, reactive } from 'vue'

  export default {
    setup() {
      const count = ref(0)
      const object = reactive({ foo: 'bar' })

      // expose to template
      return {
        count,
        object
      }
    }
  }
</script>

And according to setup usage with template :

If setup returns an object, the properties on the object can be accessed in the component's template, as well as the properties of the props passed into setup

Conclusion

the object returned in setup function is different than that return from data property in option api