Computed Property Caching in Ember

Will Raxworthy
AlphaSights Engineering
4 min readFeb 29, 2016

When it comes to Ember I feel like I learn something new everyday. For me the biggest “Aha” moments come during refactoring. Seeing something that doesn’t “feel” right and trying to figure out why and then how to make it better. One example that continually crops up for us is computed properties.

The caching behaviour of a computed property had always been a little bit mysterious to me. Thankfully though, a discussion with Miguel Camba helped me understand how they work and righted a long held assumption I had about what was actually happening.

We’re all familiar with what a computed property looks like, here’s a contrived example:

name: computed('person.name', function() {
return get(person, 'name');
});

A computed property’s value is cached and unless the value of the watched property changes the inner function won’t run each time. In the example above this wouldn’t be so bad, but in a larger example it’s unnecessary work for you application. The important thing to remember is that you must always return a value. There are many different types of computed properties depending upon your requirements, in this default state, when you set to the property:

set(this, 'name', 'will');

It will override the ember computed property you previously set and the value will never recompute if the watched key changes. Here is an ember-twiddle to demonstrate what I mean.

You’ll notice that when you set the computed property directly the value of the property fullName is replaced by the new value and the value won’t be recomputed when the watched key changes. Let’s look at how we can hook in to the way a computed property is cached and make it more explicit.

When ever you define a computed property you also have the ability to add a get() and set() inner function to it. This will give us much more fine grained control over the cp’s value caching. You would write it like this:

name: computed('person.name', {
get() {
get(this, 'person.name');
},
set(_, value) {
return value;
})
});

So what’s happening here? In each of these inner functions we are returning the value to be cached. The first time this property is inspected the property cache is empty, so it will run the inner get() function and return the persons first name. This will then be the cached value and the first name will be returned upon subsequent gets.

There are two ways to change the cached value.

The first is to invalidate the cache property by changing the watched key (person.first). When the property is next accessed the inner get() function will run and the new value at ‘person.first’ will be returned and cached.

The second is to set() the cached value. If we were to do the following:

set(this, 'name', 'Will');

The inner set() function will be called, it has two arguments, key and value. We can ignore key, and all we need to do is return the value (in this case it will be ‘Will’). This will replace the cached value and when you access the computed property, the value “Will”, will be returned.

A word of warning here, if you set one of the dependent keys in the set() function this will not trigger an invalidation of the cache. You must also always return something in the setter function, not returning will mean that “undefined” will get cached as the value of that property.

This, for me, was the key thing I was missing about computed properties and these two inner functions. All you’re doing is returning the value that is to be cached inside the property.

If you were to set another value on the property then that would be cached and replace the value ‘Will’. If you were to invalidate the cache by changing the watched key, the get() function would run and the property value would be recomputed based off ‘person.name’ and that value would be cached.

Check out this ember twiddle for an example:

In this example we also have a button to reset the form that invalidates the cache on the computed property causing the get function to be run again and return the initial value for caching.

This new understanding of computed properties has really simplified the way we change state in our components and makes resetting to the components initial state trivial. This type of functionality is especially useful in forms where you don’t want to have shadow properties through out your component and you want to easily reset state should the user cancel.

--

--