Telerik blogs

Computed properties can extend reactivity so that a value is updated when another property changes. Let’s see how this works.

One of the most powerful tools in the Vue framework is arguably computed properties. They allow us to extend the concept of reactivity by giving us a way to execute functions that create a new value depending on other properties.

For the context of this article, we will focus on understanding computed properties exclusively in the Options API. But don’t worry—the way that computed properties work on the Composition API is absolutely the same with a bit of a syntax difference, so everything you learn here will transfer cleanly.

What Exactly Is a Computed Property?

Before we start learning how to use computed properties, we have to understand first what they are and how we can use them inside our code.

Let’s use the example of calculating someone’s age based on their birthdate. In the context of Vue, you may have either a prop or a data property holding the person’s birthdate, so let’s start there.

<template>
  <div>
    <input type="date" v-model="birthday" />
    
    <p>You are X years old</p>
  </div>
</template>

<script>
export default {
  data: () => ({
    birthday: '2000-01-01'
  })
}
</script>

We are storing a data property called birthday and using a v-model to bind the value of the input to it. Whenever the user changes the date, it will be stored in the birthday data property.

After the input, we have a <p> that will display the number of years old the person is. If you don’t know computed properties, you may be tempted to create a method to calculate the age of the person and display it.

<template>
  <div>
    <input type="date" v-model="birthday" />
    
    <p>You are {{ calculateAge(birthday) }} years old</p>
  </div>
</template>

<script>
export default {
  data: () => ({
    birthday: '2000-01-01'
  }),
  methods: {
    calculateAge(birthday) {
      return new Date(new Date() - new Date(birthday)).getFullYear() - 1970
    }
  }
}
</script>

We created a calculateAge function which takes a parameter birthday and calculates the number of years since that date and today. The result of this calculation is then printed out through interpolation on Line 5 to display the user’s age.

While this “works,” this is a very common mistake. The problem with this approach is that every time the app has to re-render the content of the HTML it will have to recalculate the user’s birthday.

In a small app with a simple function like this one, it definitely will not be impactful to the performance, but the more complex an app becomes this certainly becomes a consideration.

What we want is a value that is calculated whenever another property changes. Computed properties to the rescue!

The Correct Approach with a Computed Property

Let’s take the code above and transform it to use a computed property instead. Every time the birthday value changes, this computed property should recalculate its value.

<template>
  <div>
    <input type="date" v-model="birthday" />
    <p>You are {{ userAge }} years old</p>
  </div>
</template>
<script>
export default {
  data: () => ({
    birthday: "2000-01-01",
  }),
  computed: {
    userAge() {
      return (
        new Date(new Date() - new Date(this.birthday)).getFullYear() - 1970
      );
    },
  },
};
</script>

Note that we removed the method entirely—we don’t need to keep it around since all the logic can safely go inside our computed property. Most computed properties will include a fair amount of logic. They use a value (or set of values) from another part of your component and calculate a new value.

Since this is now a computed property we name it like we would a data property, userAge instead of calculateAge. Notice that in Line 4 the interpolation makes perfect sense.

Additionally notice that in Line 15 inside the computed property we use this. to access the birthday data property. Computed properties do not accept parameters.

But How Does It Work?

Computed properties are very smart and are aware of how they calculate themselves. It is beyond the scope of this article to understand the internal mechanisms that power this, but it suffices to understand that any reactive values that we use inside a computed property will become its dependencies.

In this case, when userAge first executed its logic, it registers that the reactive value birthday was used. Now, whenever birthday changes (since its a dependency), it will signal the userAge computed property to recalculate.

Note that these dependencies can be any type of reactive value, even other computed properties. A computed property can also include several different dependencies, when any of them update the computed property will be recalculated.

Now even if the app needed to re-render the p tag containing our computed property, the function calculating the age of the user would not need to be re-executed since computed property results are cached until a new execution happens. What this means is that if the calculation gave a result of 35 the first time around, it will always display the cached value of 35 until the birthday changes. At this point, the computed will re-execute and the new result will be cached in its place.

An Important Gotcha

At some point you may be tempted to “write” a value into a computed property, say for example:

methods: {
  submitUser () {
    // Round the user age to the nearest decade
    this.userAge = Math.round(this.userAge / 10) * 10)
  }
}

You should never mutate a computed property. Remember that if you need to calculate a different value from a computed property, you can always create another computed property for this purpose.

<template>
  <div>
    <input type="date" v-model="birthday" />
    <p>You are {{ userAge }} years old</p>
  </div>
</template>
<script>
export default {
  data: () => ({
    birthday: "2000-01-01",
  }),
  computed: {
    userAge() {
      return (
        new Date(new Date() - new Date(this.birthday)).getFullYear() - 1970
      );
    },
    userAgeRoundedToDecade () {
      return Math.round(this.userAge / 10) * 10
    }
  },
};
</script>

Now we created the userAgeRoundedToDecade computed property, which we can use anywhere we need. This new computed has userAge as a dependancy, so anytime birthday updates, userAge will update, which will in turn make userAgeRoundedToDecade update. This type of changing is very common and very powerful.

Wrapping Up

Computed properties are an incredibly powerful tool in Vue and very easy to get started with.

I do have to confess, however, that there is a way to make a computed property writable through getters/setters. This is a fairly advanced way to work with computed properties and I would not recommend it until you are absolutely comfortable with Vue and know exactly how and more importantly why you want to use it. It’s not something you will encounter commonly so no need to worry too much about it for now, just know that it is a thing that exists.


About the Author

Marina Mosti

Marina Mosti is a frontend web developer with over 18 years of experience in the field. She enjoys mentoring other women on JavaScript and her favorite framework, Vue, as well as writing articles and tutorials for the community. In her spare time, she enjoys playing bass, drums and video games.

Related Posts

Comments

Comments are disabled in preview mode.