Use arrow function in vue computed does not work

Javascriptvue.jsVuejs2

Javascript Problem Overview


I am learning Vue and facing a problem while using arrow function in computed property.

My original code works fine (See snippet below).

new Vue({
  el: '#app',
  data: {
    turnRed: false,
    turnGreen: false,
    turnBlue: false
  },
  computed:{
  	switchRed: function () {
    	return {red: this.turnRed}
    },
    switchGreen: function () {
    	return {green: this.turnGreen}
    },
    switchBlue: function () {
    	return {blue: this.turnBlue}
    }
  }
});

.demo{
  width: 100px;
  height: 100px;
  background-color: gray;
  display: inline-block;
  margin: 10px;
}
.red{
  background-color: red;
}
.green{
  background-color: green;
}
.blue{
  background-color: blue;
}

<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.2.4/vue.js"></script>
<div id="app">
  <div class="demo" @click="turnRed = !turnRed" :class="switchRed"></div>
  <div class="demo" @click="turnGreen = !turnGreen" :class="switchGreen"></div>
  <div class="demo" @click="turnBlue = !turnBlue" :class="switchBlue"></div>
</div>

However, after I change methods in computed property, the color will not change (though the turnRed value still switch between true and false successfully).

This is my code:

computed:{
  	switchRed: () => {
    	return {red: this.turnRed}
    },
    switchGreen: () => {
    	return {green: this.turnGreen}
    },
    switchBlue: () => {
    	return {blue: this.turnBlue}
    }
}

Do I use the wrong syntax ?

Javascript Solutions


Solution 1 - Javascript

You are facing this error because an arrow function wouldn't bind this to the vue instance for which you are defining the computed property. The same would happen if you were to define methods using an arrow function.

> Don’t use arrow functions on an instance property or callback (e.g. vm.$watch('a', newVal => this.myMethod())). As arrow functions are bound to the parent context, this will not be the Vue instance as you’d expect and this.myMethod will be undefined.

You can read about it here.

Solution 2 - Javascript

The arrow function lost the Vue component context. For your functions in methods, computed, watch, etc., use the Object functions:

computed:{
    switchRed() {
        return {red: this.turnRed}
    },
    switchGreen() {
        return {green: this.turnGreen}
    },
    switchBlue() {
        return {blue: this.turnBlue}
    }
}

Solution 3 - Javascript

You can achive this by deconstructing what you want from this:

computed:{
  switchRed: ({ turnRed }) => {red: turnRed},
  switchGreen:  ({ turnGreen }) => {green: turnGreen},
  switchBlue: ({ turnBlue }) => {blue: turnBlue}
}

Solution 4 - Javascript

And why not something simpler like this?

new Vue({
  el: '#app',
  data: {
    turnRed: false,
    turnGreen: false,
    turnBlue: false
  },
  methods:{
    toggle (color) {
      this[`turn${color}`] = !this[`turn${color}`];
    }
  }
});

.demo{
  width: 100px;
  height: 100px;
  background-color: gray;
  display: inline-block;
  margin: 10px;
}
.red{
  background-color: red;
}
.green{
  background-color: green;
}
.blue{
  background-color: blue;
}

<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.2.4/vue.js"></script>
<div id="app">
  <div class="demo" @click="toggle('Red')" :class="{red:turnRed}"></div>
  <div class="demo" @click="toggle('Green')" :class="{green: turnGreen}"></div>
  <div class="demo" @click="toggle('Blue')" :class="{blue: turnBlue}"></div>
</div>

Solution 5 - Javascript

When creating computed you do not use => , you should just use switchRed () {...

Take a look at snippet. Works as it should.

It applies to all computed,method, watchers etc.

new Vue({
  el: '#app',
  data: {
    turnRed: false,
    turnGreen: false,
    turnBlue: false
  },
  computed:{
  	switchRed () {
    	return {red: this.turnRed}
    },
    switchGreen () {
    	return {green: this.turnGreen}
    },
    switchBlue () {
    	return {blue: this.turnBlue}
    }
  }
});

.demo{
  width: 100px;
  height: 100px;
  background-color: gray;
  display: inline-block;
  margin: 10px;
}
.red{
  background-color: red;
}
.green{
  background-color: green;
}
.blue{
  background-color: blue;
}

<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.2.4/vue.js"></script>
<div id="app">
  <div class="demo" @click="turnRed = !turnRed" :class="switchRed"></div>
  <div class="demo" @click="turnGreen = !turnGreen" :class="switchGreen"></div>
  <div class="demo" @click="turnBlue = !turnBlue" :class="switchBlue"></div>
</div>

Solution 6 - Javascript

I don't know if this will backfire in future but apparently arrow functions used in vue object properties, receive the this context as their 1st argument:

props: ['foo'],

data: (ctx) => ({
  firstName: 'Ryan',
  lastName: 'Norooz',
  // context is available here as well like so:
  bar: ctx.foo
}),

computed: {
  fullName: ctx => ctx.firstName + ' ' + ctx.lastName // outputs: `Ryan Norooz`
}

this way you can still access everything in the component context just like this !

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionPJCHENderView Question on Stackoverflow
Solution 1 - JavascriptAmresh VenugopalView Answer on Stackoverflow
Solution 2 - Javascriptthrorin19View Answer on Stackoverflow
Solution 3 - JavascriptMarius LianView Answer on Stackoverflow
Solution 4 - JavascriptSoldeplata SaketosView Answer on Stackoverflow
Solution 5 - JavascriptMarek UrbanowiczView Answer on Stackoverflow
Solution 6 - JavascriptRyan NoroozView Answer on Stackoverflow