Vue computed property not updating as expected

Viewed 13584

I have a count

  You have {{ keywordsLeft }} keywords left

and in my Vue.

var u = new Vue({
    el:'#app',
    data:{
        keywords:keywords,
        user:user,
        total_keywords:user.total_keywords,
    },
    computed: {
        keywordsLeft: function () {
           var left = total_keywords-keywords.length;
           if(left<=0){
             return 0;
           } else {
             return left;
           }
       }
   }, 
   methods: {
       deleteKeyword: function(keyword){
           var i = this.keywords.indexOf(keyword);
           this.keywords.splice(i,1);
       }
   }
       // rest of VUE code.

Whenever an update is made to the data, the computed value doesnt update. I have to refresh the page to do it. I thought the purpose of computed property was live reactivity? Am I missing something?

Do I need to explicitly update it somehow? recompute is manually? Forgive me if Im being stupid, but this seems like a waste if its not done automatically. Might as well just use jquery then lol

3 Answers

Computed properties are computed automatically and they're what you're supposed to use in a case like this.

I guess the problem lies somewhere in the code that updates your actual data. It is doing it in a way that doesn't work with Vue's reactivity system. This happens to many people :)

I suggest you to read this for overall guide https://v2.vuejs.org/v2/guide/reactivity.html and this https://v2.vuejs.org/v2/guide/list.html#Array-Change-Detection for gotchas on arrays.

Added:

I see your problem now. Inside the keywordsLeft computed property you're not referring to the Vue instance's data but actually to the original data. What I mean the keywords.length refers to the original keywords object and its length. Same with total_keywords - it doesn't refer to the Vue instance's data. You should use this.keywords and this.total_keywords.

I always keep the instance data property names different from the outside/global/not-Vue data. I'm saying something like this is better:

data: {
  myKeywords: keywords,
  myTotalKeywords: totalKeywords
  ...
}

This way you cannot mix what you're referring to. Now you should refer to this.myKeywords etc. inside the Vue instance's scope.

K I solved it by just moving it into a method. Even though I feel this is wrong, it works. Not really sure the purpose of computed properties now. Since they seem to only compute once?

Anyway heres how I fixed it.

The HTML

You have {{ keywordsLeft() }} keywords left

The Vue

var u = new Vue({
el:'#app',
data:{
    keywords:keywords,
    user:user,
    total_keywords:user.total_keywords,
},
methods: {
    keywordsLeft: function () {
       var left = total_keywords-keywords.length;
       if(left<=0){
         return 0;
       } else {
         return left;
       }
   }
}, 

Super simple.

This problem happened frequently for me.

To solve that, I tried to add key as that computed value and it worked perfectly.

 <span :key="keywordsLeft">
    You have {{ keywordsLeft() }} keywords left
 </span>
...
 computed: {
        keywordsLeft: function () {
           var left = total_keywords-keywords.length;
           if(left<=0){
             return 0;
           } else {
             return left;
           }
       }
   }, 

When key is changed, that component(dom) will be re-rendered.

Related