cft
Become a CreatorSign inGet Started

You Shouldn’t be Using Mixins in Vue.js Anymore

Learn about the disadvantages of Mixns and how you can use the Composition API to migrate from them


user

Eyuel Berga Woldemichael

4 months ago | 4 min read
Follow

shouldnt-using-mixins-vuejs-anymore-ginlz

Mixins were the go-to way of sharing reusable logic between components in Vue 2. But now with the availability of the Composition API, we can achieve code reusability in a much cleaner way.

What are mixins?

Mixins are a way of reusing a functionality between components by merging the properties of a mixin with the component. It allows us to define a certain logic once and use it on multiple components.

Why Shouldn’t I use mixins?

Well, mixins are generally not the ideal way of sharing logic because they come with some drawbacks:

Overlapping names

Naming conflicts often arise with mixins. During the merging process, conflicting properties between mixins and components get overridden based on some merge strategy. This can lead to some unexpected behaviors in our application.

Tight coupling

It is not uncommon to encounter implicit dependencies between a mixin and a component. This makes it excruciatingly hard to refactor the component or mixin without breaking the existing code. Mixins can also get tightly coupled to other mixins over time as new requirements emerge.

Hard to understand and debug

It is very difficult for a new developer to understand a code-base with lots of mixins. It is not obvious from where a property is coming from, especially if there are globally registered mixins. Isolating a bug also becomes a nightmare as the application grows.

Replacing mixins with composable

Let’s start with an example to illustrate some of the flaws of mixins and how we would solve these issues with the Composition API.

Say we have a simple count-down timer to wait for some seconds before showing the main contents of a component. We have extracted this logic to a mixin called countDownMixin . This is what the code looks like:

const countDownMixin = {

data() {

return {

countDown: 10,

interval: null,

};

},

computed: {

isDone: function () {

return this.countDown <= 0;

},

},

methods: {

decrement() {

if (this.countDown > 0) this.countDown -= 1;

},

},

mounted: function () {

this.interval = setInterval(this.decrement, 1000);

},

unmounted: function () {

clearInterval(this.interval);

},

};

export default countDownMixin;

By registering this mixin, we will be able to “mix” the count-down logic to the component.

<template>

<div>

<h1 v-if="!isDone">Countdown: {{ countDown }} seconds</h1>

<h1 v-else>Hello World</h1>

</div>

</template>

<script>

import countDownMixin from "./countDownMixin";

export default {

name: "HelloWorld",

mixins: [countDownMixin]

};

</script>

This seems to be a neat solution at first glance, but what if I want to wait for 20 seconds instead of 10?

Well, one solution would be to override the initial state of countDown from the consuming component. By doing that, we explicitly create conflicting names knowing that the data property defined in the component will be given top priority.

<template>

<div>

<h1 v-if="!isDone">Countdown: {{ countDown }} seconds</h1>

<h1 v-else>Hello World</h1>

</div>

</template>

<script>

import countDownMixin from "./countDownMixin";

export default {

name: "HelloWorld",

mixins: [countDownMixin],

data(){

return {countDown:20}

}

};

</script>

This solution works, but having the ability to do this makes the mixin vulnerable to unintentional replacement of other options too. Now, if the component defines a decrement method without knowing that the countDownMixin also has a method with the same name, the component will not work as expected anymore.

<template>

<div>

<h1 v-if="!isDone">Countdown: {{ countDown }} seconds</h1>

<h1 v-else>Hello World</h1>

</div>

</template>

<script>

import countDownMixin from "./countDownMixin";

export default {

name: "HelloWorld",

mixins: [countDownMixin],

data(){

return {countDown:20}

},

methods:{

decrement() {

console.log("decrement from self");

}

},

};

</script>

Although the decrement method is defined for the purpose of internal use only, it is not possible to avoid it from being overridden.

A better solution with the Composition API

import { ref, computed, onMounted, onUnmounted, readonly } from "vue";

export default function useCountDown(countDownDuration = 10) {

const countDown = ref(countDownDuration);

const interval = ref(null);

const decrement = () => {

if (countDown.value > 0) countDown.value -= 1;

};

const isDone = computed(() => {

return countDown.value <= 0;

});

onMounted(() => {

interval.value = setInterval(decrement, 1000);

});

onUnmounted(() => {

clearInterval(interval.value);

});

return { countDown:readonly(countDown), isDone };

}

By using the Composition API, we avoid all of the problems we faced with mixins. The useCountDown composable takes as a parameter the count down duration. There is no need to override anything and dependencies are neatly organized as parameters of the composable function.

It also exposes properties only intended to be used by the consuming component and the internal states are protected, so the component cannot accidentally alter the behavior of the composable.

Name overlaps are also not an issue, as we are required to explicitly provide names for all the returned variables of the composable.

<template>

<div>

<h1 v-if="!isDone">Countdown: {{ countDown }} seconds</h1>

<h1 v-else>Hello World</h1>

</div>

</template>

<script>

import useCountDown from "./useCountDown";

export default {

name: "HelloWorld",

setup(){

const {countDown, isDone} = useCountDown();

return {countDown, isDone};

}

};

</script>

Closing thoughts

The Composition API makes code reuse very easy and straightforward. I hope you found this article convincing enough to migrate from mixins and embrace the power of composable. If you need more information on the Composition API, please check out the Official Docs. Thank you for reading.

Upvote


user
Created by

Eyuel Berga Woldemichael

Follow

Software Developer

I am a Software Developer currently based in Addis Ababa, Ethiopia. I am passionate about Web and Mobile Development.


people
Post

Upvote

Downvote

Comment

Bookmark

Share


Related Articles