Understanding computed properties in VueJS

computed properties in Vue

In this post we’ll see what are computed properties, how to write a computed property and let’s understand how a computed property is designed behind the scenes.

Don’t forget to visit the links in the references section.

What are the computed properties?

Computed properties are like normal methods in Vue, but with the caching ability on the reactive dependencies.

An example of computed property

We have a grocery list of items and we’d like to display the total price of the items. So, when changing the price of an item, the total gets updated too.

Here’s the grocery list component.

<template>
 <div>
  <table>
   <thead>
    <tr>
     <th>Name</th>
     <th>Price</th>
     <th>Quantity</th>
    </tr>
    <tr></tr>
    <tr></tr>
   </thead>
   <tbody>
    <tr v-for=’item in groceries’ v-bind:key=’item.id’>
     <td>{{item.itemName}}</td>
     <td><input type=’number’ v-model=’item.price’ min=’0’ /></td>
     <td><input type="number" v-model=’item.quantity’ min=’1’/></td>
    </tr>
    <tr>
     <td>
     Total Price:
     </td>
     <td><b>${{ groceriesCount }}</b></td>
     <td></td>
    </tr>
   </tbody>
  </table>
 </div>
</template>
<script>
export default {
 name: ‘GroceriesList’,
 data () {
  return {
   groceries: [
    { itemName: ‘Rice’, price: 20, quantity: 1 },
    { itemName: ‘Flour’, price: 80, quantity: 1 },
    { itemName: ‘Oats’, price: 40, quantity: 1 }
   ]
  }
 },
 computed: {
  groceriesCount: function () {
   return this.groceries.reduce(
    (acc, val) => acc + parseInt(val.price),
    0
   )
  }
 }
}
</script>

For our convenience, I’ve textboxes for displaying price of the items so we can directly edit and see the computed property in action.

Notice the grocieriesCount computed property, we haven’t included the quantity of the items yet. But, now try to change the quantity of the items and observe the total price. The total price will only change when changing the price of any item. So, the price is a “reactive dependency” for the computed property.

Test for computed property caching

In the above example, we’ve just used the total price of the groceries only once.

Let’s use the computed property in a h2 tag and try to change the price of any item.

  <h2>Total price: ${{groceriesCount}}</h2>

After adding the above h2 tag in the template section, run the app.

Open developer tools and place a breakpoint in our computed property (groceriesCount). Now, try to change the price of any grocery item and your breakpoint should hit only once.

Though we have used the computed property (groceriesCount) twice in the template our breakpoint got hit only once. This is the caching ability of the computed properties.

Computed setters

By default computed properties are getters but we can have setters.

computed: {
 fullName: {
  get: function() {
   return this.firstName + this.lastName;
  },
  set: function(value) {
   let names = value.split(‘ ‘);
   this.firstName = names[0];
   this.lastName = names[names.length - 1];
  }
 }
}

If we set the fullName in a method, the firstName and lastName props will correctly.

How do computed properties work under the hood?

Vue has a dependency tracking system, which tracks the reactive dependencies.

The data in a component converts into getters and setters. So, it is not specific to the computed properties. Vue creates the getters and setters for every property in the data() function.

Here’s how reactivity works: When accessing a value via getter, it will add to the dependencies and when changing a value via a setter, the setter will notify who depends on the property and re-renders the DOM.

Here’s the sample code to understand how the reactivity works for a property.

function defineReactive (obj, key, val) {

  const dep = new Dep();

  Object.defineProperty (obj, key, {
    get: function () {
      // check if it’s not already a dependancy
      if (dep.target)
        buildDependants(val); // This will push the dependencies to some array to use it later      

      return val;
    },
    set: function (newVal) {
      const value = currentValue; //current value of prop
      // some basic validations to perform
      validations();

      // set the newVal 
      value = newVal;

      // notify the depedencies
      // This will notify all depending props
      dep.notify(); 
    }
  })
}

The Dep() function is a dependancy tracker in Vue.

As you can see, we capture the dependencies in the get function and we’ll notify all the depending properties when changing the value in set function via dep.notify().

Here’s the source code that converts properties to getters and setters.

References