How to Avoid Shared States Between Mixins in Ember.js

Photo of Jakub Niechciał

Jakub Niechciał

Updated Jan 5, 2023 • 5 min read

Personally, I use mixins a lot.

With their help we can easily separate responsibilities in an object (it doesn’t matter whether it’s a controller, a component or something else) and they let us think in terms of designing the app’s architecture in a reusable way. Mixins can make our code cleaner, but they also bring the danger of states being shared between multiple objects. You might even be unaware of this, as it’s not a very common situation, but if it happens, looking for that bug may cost you a few hours - so it’s better to be prepared!

Shared States Between Mixins in Ember.js

What is a shared state?

What do I mean by a shared state? Let’s say we have two components, both storing a list, one of which is ‘tasks’ and the other is ‘items to buy’. We decide to extract the array storing that list to a mixin (this might seem like overkill, but it’s only an example!):

Next, we create our two components with the ability to add a new task or shopping item with a predefined name. Take a look at this Ember Twiddle to see a live demo and play around with adding items.

As you can see, it looks like they are sharing the same array instance between them, which leads to buggy behavior. This bug is pretty easy to spot, but suppose you’ve rendered two completely different components, which share only part of their responsibilities through a mixin, in two different routes. For some users the app will crash on the first route after they’ve visited the second, and for others on the second after visiting the first. Moreover, it’ll be difficult to reproduce the crash.

Why does this happen?

First of all, let’s try to understand why this happens. If we are more familiar with Object Oriented Programming rather than Prototype Inheritance, we might assume that creating two components that extend the same mixin cannot possibly lead to a shared state by accident. When we define a new mixin, and eventually import it to another file (like a component), it is being created. During this creation, the array that we defined for the list property is also being created and list is storing a reference to this array. When we call, say, Ember.Component.extend(OurMixin, { (...) }), we basically create a new Ember.Component instance that copies enumerable properties from the base Ember.Component instance, from OurMixin instance and from the object that we defined as our component (last in order, and highest in priority).

Regular properties that we define in mixins are enumerable, so the value of the list property (a reference to an array instance) is copied to our component. The same thing happens in any other place we use our mixin - the same reference to the same array is copied to a new object and this is the origin of the bug. There is no blueprint like a class object in Object Oriented Programming. There is only the first instance from which enumerable properties are copied to extending objects.

Avoiding shared states by using computed properties

The easiest way to avoid shared states amongst objects implementing mixins is to wrap the objects stored by reference (plain objects, arrays) into computed properties with a simple getter:

Here, what is being copied is a function, not an array reference. In each object this computed property will return a new instance of Ember.A() the first time it’s evaluated and won’t change that instance at any point in the main object’s life.

Avoiding shared states using #init()

Another way to avoid shared states is to define properties stored by reference in an #init method in the mixin.

With this, every object that implements the mixin will evaluate its own #init method and thus create its own array instance. However, this solution is a little bit risky in terms of forgetting this._super and is somewhat hard to read by just scanning the code.

Wrapping up

Mixins are great and I think they are one of the most useful features in Ember for code maintenance. Just be sure that you understand what is going on, and how it complies with JavaScript Prototypal Inheritance.

Have you bumped into similar pitfalls? Would you like to share your experiences? Please do so in the comments below! And if you liked my article, you might be also interested in how to add a catch blog for returned promise in Ember.

Photo of Jakub Niechciał

More posts by this author

Jakub Niechciał

Jakub has obtained a Master’s degree at Poznań University of Technology in Control Engineering and...
How to build products fast?  We've just answered the question in our Digital Acceleration Editorial  Sign up to get access

We're Netguru!

At Netguru we specialize in designing, building, shipping and scaling beautiful, usable products with blazing-fast efficiency
Let's talk business!

Trusted by: