Home > OS >  Why is empty object in Vue3 not reactive?
Why is empty object in Vue3 not reactive?

Time:03-14

When I create a ref from an empty object and later add object properties, there is no reactivity:

<template>
  <p>{{hello}}</p>
  <button @click="add()">Click me</button>
</template>

<script>
import {ref} from 'vue';
export default {
  name: "Test",
  setup(){
    const myData = ref({});
    return {myData}
  },
  methods: {
    add(){
      this.myData["text"] = "Hello";
      console.log(this.myData);
    }
  },
  computed: {
    hello(){
      if(this.myData.hasOwnProperty("text")) return this.myData["text"];
      return "no text";
    }
  }
}
</script>

Clicking the button shows that myData has changed but the computed property hello does not update.

Also tried reactive({}) instead of ref({}) without success.

What works is this const myData = ref({"text": "no text"});. But why does the empty object not work?

CodePudding user response:

Try to you update your ref object like

this.myData = {"text": "Hello"}

const { ref, computed } = Vue
const app = Vue.createApp({
  /*setup(){
    const myData = ref({});
    const hello = computed(() => myData.value.hasOwnProperty("text") ? myData.value.text : myData.value = "no text")
    const add = () => {
      if(Object.keys(myData.value).length === 0) {
        myData.value = {'text': "Hello"};
      } else {
        myData.value.otherProperty = "Hello again"
      }
    }
    return { myData, add, hello }
  },*/
  setup(){
    const myData = ref({});
    return { myData }
  },
  methods: {
    add(){
      if(Object.keys(this.myData).length === 0) {
        this.myData = {"text": "Hello"}
      } else {
        this.myData.otherProperty = "Hello again"
      }
      console.log(this.myData)
    },
  },
  computed: {
    hello(){
      return Object.keys(this.myData).length !== 0 ? this.myData[Object.keys(this.myData)[Object.keys(this.myData).length - 1]] : "no text"
    }
  }
})
app.mount('#demo')
<script src="https://unpkg.com/[email protected]/dist/vue.global.prod.js"></script>
<div id="demo">
  <p>{{ hello }}</p>
  <button @click="add">Click me 2 times</button>
</div>

CodePudding user response:

If you change your computed property to be defined such that it references myData['text'] directly before returning, things work as expected:

  computed: {
    hello() {
      return this.myData['text'] || 'no text'; // works
    }

I suspect what's going on is that hello is being called by vue before the text property exists on the object, and because the function returns before actually touching the proxied value (it short-circuits as soon as it sees that hasOwnProperty has returned false) it isn't able to notice the dependency on this.myData.

  • Related