Functional Computed Properties in Vue Components
Tags: vue, javascript, functional, productivity
Published: 2020-09-14 17:39:12

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 templates[^1]. 

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:

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

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

```js
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:

```js
// 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](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions), 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:

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

Javascript's [magical](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments) `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:

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

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

```js
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:

```js
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:

```vue
<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](https://vuejs.org/v2/guide/computed.html)