Home » vue » Vue is there a correct way to use Computed Property setters without a data variable?

Vue is there a correct way to use Computed Property setters without a data variable?

Posted by: admin November 26, 2021 Leave a comment

Questions:

Relatively new to Vue and not sure whether there’s a more correct way of doing this with computed properties or I should use a watcher. Mostly the docs seem to almost discourage watchers so I’m looking for clarification.

My example:

export default {
  name: "myComponent",
  props: {
    frameExpanded: {
      type: Boolean,
      required: true
    }
  },
  computed: {
    isExpanded: {
      get: function() {
        return this.frameExpanded;
      },
      set: function(newValue) {
        this.frameExpanded = newValue;
      }
    }
  }
};

And use this.isExpanded = !this.isExpanded for interactions. This works correctly; However, throws a warning:

[Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop’s value. Prop being mutated: “frameExpanded”

Rightfully it seems wrong to mutate a prop. Attempting to do this more “correctly” I might try to do the following

export default {
  name: "myComponent",
  props: {
    frameExpanded: {
      type: Boolean,
      required: true
    }
  },
  data() {
    const selfExpanded = true;

    return { selfExpanded };
  },
  computed: {
    isExpanded: {
      get: function() {
        return this.frameExpanded && this.selfExpanded;
      },
      set: function(newValue) {
        this.selfExpanded = newValue;
      }
    }
  }
};

However, this does not work in one important case for me, when frameExpanded = false trying to have the individual component expand is prevented due to the logical and, which is why I implemented it the first way. This leads me to believe what I’m looking to use is Watch.

Though more generally I’d like to better understand these, so when you want a component to be reactive to a prop and have a mutate it I should use Watch with a data variable?

Answers:

One solution would be to implement v-model

https://vuejs.org/v2/guide/components.html#Using-v-model-on-Components

Vue.config.devtools = false;
Vue.config.productionTip = false;

const myComponent = Vue.component('my-component', {
  template: `
    <button @click="toggle">Toggle</button>
  `,
  name: "myComponent",
  props: {
    value: {
      type: Boolean,
      required: true
    }
  },
  methods: {
    toggle() {
      this.$emit('input', !this.value);
    }
  }
})


var app = new Vue({
  el: '#app',
  components: {
    myComponent
  },
  data() {
    return {
      frameExpanded: false
    }
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  <h1 v-if="frameExpanded">Hello World !</h1>
  <my-component v-model="frameExpanded"/>
</div>

###

Whenever you want your prop to be reactive and change it, do it through emit.
As the Vue-warning + it is the best practice, each component will change it’s own data properties. So you want to change data of parent from child (which is a prop for child), let parent do it. You just return a new value with emit’s 2nd parameter.

// in child
this.$emit('actionWillChangeTheProp', newValueOfProp);
// in parent
<parent-component @actionWillChangeTheProp="updateDataFromParent"></parent-component>

methods: {
    updateDataFromParent(newValue) {
        this.parentData = newValue
    }
}