The mixin trap

Taufan
4 min readJan 18, 2021

I am pretty pissed right now as I just spent about one and a half hours hunting down the exact place a value was defined in a Vue 2 app I am maintaining. Needless to say, it was very unpleasant.

I was trying to refactor the code, extracting out a component from a page to be reusable. As I was moving the code around, the console logged an error:

[Vue warn]: Invalid prop: type check failed for prop "myProp". Expected String with value "undefined", got Undefined

The code shows the expected value seems to be coming from a computed value in a mixin. I opened the mixin, and the function is just returning another value: this.type. So I look around for any declaration of type in the data and props. I got nothing.

Long sigh…

Well, hello darkness my old friend.

Immediately, I know this would be a long search. There must be some “optimization” taken by the previous coder in the name of “best practice”.

I opened three of the seven mixins (SEVEN!) used by the component, as those were the best candidates for the declaration I’m looking for. I still got nothing.

I then follow the mapGetters and mapActions calls inside those mixins. There seems to be a parameter of type object with key type being passed to an action. Maybe the type is declared in the store? So I checked the function and seems like the parameter was just being passed onto an API call. Also, I don’t see any mapGetters with variable type. So I’m back to square one.

At this point I am about to give up, close the editor, remove the clone from my laptop, open LinkedIn and TIA Jobs for opportunities, and saying bye bye to my colleagues. I mean what is the point of doing all this?!

But then I noticed something. The value for the type in the action method call is assigned by using a string notation, using a constant value. Could it be that the type definition is done similarly?

I then tried to narrow down my search by turning on and off the mixins one by one. I will just activate one mixin at a time, and log the this.type value. If it has a value, then I can confidently say that it was defined within the current active mixin. From there, I can find the constant-value-kind-of-object-property-definition.

Luckily, the first mixin I tried yielded the expected result.

I looked into the mixin and see the props being defined in this weird way:

[someConstants[`${prefix}_TYPE_FILTER`]]: {
type: String,
required: false,
default: "my_type",
},

And indeed, the variable’s value is "type". But that is only half of the problem. If it is a props, who is passing the value? Since the component using the mixin is a page, I immediately looked into the routes definition. There is no explicit config to pass the type value. But there are a few function calls, one of which return a config object with type as one of its keys.

I was relieved to be able to find that.

But I also want to punch my screen, in hope the punch would travel in time through the commits to when the original code was written, and all the way onto the original author’s face. Just so the author understand that the “optimization” is shit.

Mixin is bad for scaling. I don’t see why we still use mixin when we are writing bigger code base for our apps.

Mixin hides everything from you. There is no way to immediately know which value is supplied by which mixin. Actually, there is one way:

mixins: [yoMixinIsOld()],
computed: {
yourValue () {
// from yoMixinIsOld
return this.originalYourValue;
},
},

But even with that comment, the originalYourValue can still use a value that, guess what, is coming from another mixin that is imported in the component level. Just like what I am experiencing above.

Now tell me how does a mixin do any good for you?

Sure, the promise sounds good. You write a mixin, you can replicate the same behavior for any component using the mixin. But once you are building something more than just a todo app, mixin is just a code smell. Your code starts to become more obscure by the day. But since writing mixin is just so simple, you can’t stop. Without realizing it, now you have more mixins than components. Good luck to that other dev trying to understand your code!

The mixin case is one of the instances where I prefer React over Vue, at least over Vue 2. With React, there is no way you can modify your component’s props by using another module into your component.

The props in a way is the contract that its user have to agree to use. So you can be sure that a value is coming from wrapping component, or in case of using Redux, it may come from the wrapping mapper function. If you structure your component right, tracing how the values being passed around is straight forward!

You want to scale? Maintain clarity. It helps to improve the DX, which in turn avoid your devs getting cranky as they just wasted precious time hunting down the super-ultra-optimized-code-which-you-would-never-understand-why to find a single definition.

--

--