DEV Community

Jonathan Land
Jonathan Land

Posted on • Originally published at jonathanland.design

Functional Computed Properties in Vue Components

Computed properties are easily one of my favorite expressions of Vue's reactive paradigm. They allow us to store a reference to a result, abstracting the tracking of changes and cleaning out logic from our templates1.

Sometimes computed properties are complex units of logic, running loops, building internal data structures and finally churning out a result. More often, though, I find myself using them for simpler things — concatenating strings, multiplying one thing by another, mapping an array of objects to expose specific properties. Exactly the kinds of jobs that arrow functions were made for.

How

Functional computed properties are written in the following way:

computed: {
  fullName: ({ firstName, lastName }) => firstName + ' ' + lastName
}

Let's break that down. The regular syntax for that computed property would be:

computed: {
  fullName () { 
    return this.firstName + ' ' + this.lastName
  }
}

Not too bad, but a bit verbose. If we were writing regular old ES6, outside of a Vue component, we'd probably want to turn that into an arrow function. However, inside our component the following code wouldn't work:

// DOES NOT WORK

computed: {
  fullName: () => this.firstName + ' ' + this.lastName
}

That's because a central feature of arrow functions is that they bind their parent's lexical scope, which is a fancy way of saying that they don't have their own this. We would end up with a TypeError, telling us that this is undefined.
So we know we need to get our component's this into the function, but from where? Well, it turns out that Vue injects the whole component as the only argument of every computed property! That how we have access to this in the regular syntax. You can test this for yourself like this:

computed: {
  testThis () { 
    console.log(arguments)
  }
}

Javascript's magical arguments variable represents an array of all the arguments provided to a function. Once you load the component, you'll see in the console an array containing a single object — your Vue component, complete with your data, computed, methods, $root etc.

So now we can write our property as:

computed: {
  fullName: (this) => this.firstName + ' ' + this.lastName
}

The final touch is destructuring this, pulling in only the properties we need:

computed: {
  fullName: ({ firstName, lastName }) => firstName + ' ' + lastName
}

Why

So what have we gained? Well, for starters we've turned our 3 lines into a punchy one-liner. We've made it clear that this is a function that takes in some values and returns a new value, with no side effects. Finally, we've made it explicit what data the computed property depends on and tracks — no matter what other data is in the component, we know that this property can only depend on the values we've provided it.

If we have many computed properties, they're easier to skim and reason about:

computed: {
  fullName: ({ firstName, lastName }) => 
    firstName + ' ' + lastName,

  shortNameToDisplay: ({ firstName }) => 
    firstName || 'Please enter your name',

  isInFirstNamesList: ({ firstNamesList, firstName }) => 
    firstNamesList.indexOf(firstName) !== -1,

  // we can even call the fullName computed property, 
  // since it's available in the component as this.fullName
  nameUrl: ({ baseUrl, fullName }) => 
    baseUrl + '/' + fullName.toLowerCase(),
}

A great use case is for 'destructuring' props to simplify templates in presentational components:

<template>
  <img :src="photoUrl">
  <h3>{{ firstName }}</h3>
  <p>{{ bio }}</p>
</template>

<script>
export default {
  name: 'PersonCard',
  props: {
    person: { type: Object }
  },
  data () {
    baseUrl: 'https://foo.bar/baz'
  },
  computed: {
    firstName: ({ person }) => person.firstName,
    bio: ({ person }) => person.bio,
    photoUrl: ({ baseUrl, person }) => baseUrl + '/' + person.photo
  }
}
</script>

When

As a rule of thumb, functional computed properties (and arrow functions in general) are most useful when their logic is simple and requires no temporary variables. If you think of your property as "this thing in relation to that thing", it's probably a good candidate.

On the other hand, as soon as you find yourself adding braces to a functional property and running logic before you return, it's probably time to refactor back to the regular syntax. Remember, the improvement here is solely to code readability, so the question should always be "is this easier to understand?"


  1. As per the recommendation of the Vue style guide 

Top comments (1)

Collapse
 
chrstnfrrs profile image
Christian Farris

I've never used the destructuring approach with Computed properties before. Definitely going to start doing this!