Dependency injection in Vue: Advantages and caveats
Tags: vue, webdev
Published: 2021-07-22 18:56:23

**Written by [Emmanuel John] (https://blog.logrocket.com/author/emmanueljohn/)** ✏️

## Introduction

Dependency injection is a great pattern to use while building large and complex applications. The major challenge with building these applications is creating loosely coupled components, and this is where dependency management is most critical.

This article will introduce dependency injection, its pros and cons, and how dependency injection can be handled in Vue projects.

## What is dependency injection?

Dependency injection is a design pattern in which classes are not allowed to create dependencies. Rather, they request dependencies from external sources. This design pattern strongly holds that a class should not configure its dependencies statically.

## Why dependency injection?

Why should we use dependency injection in Vue when we can pass data from parent components down to the children components?

Some experience with using props would expose you to the term prop drilling, which is the process where props are passed from one part of the component tree to another by going through other parts that do not need the data, but only help in passing it through the tree:

```
RexComponent (Anyone needs my wallet address?)
├── TomComponent
   ├── PeterComponent
      ├── DurryComponent (yes I need it)
```

With the above snippet, let’s consider a scenario where `RexComponent` has a wallet address to give out and `DurryComponent` is the only one in need of the wallet address. We will have to pass the wallet address from `RexComponent` to `TomComponent` to `PeterComponent`, and finally to `DurryComponent`. This results in the redundant piece of code in both `TomComponent` and `PeterComponent`.

With dependency injection, `DurryComponent` would receive the wallet from `RexComponent` without passing through `TomComponent` and `PeterComponent`.

To handle dependency injection in Vue, the provide and inject options are provided out of the box.

The dependencies to be injected is made available by the parent component using the provide property as follows:

```typescript
//Parent component
<script lang="ts">
    import {Component, Vue} from 'vue-property-decorator';
    import Child from '@/components/Child.vue';
    @Component({
        components: {
            Child
        },
        provide: {
            'name': 'Paul',
        },
    })
    export default class Parent extends Vue {
    }
</script>
```

The provided dependency is injected into the child component using the injected property:

```typescript
<template>
  <h1> My name is {{name}}</h1>
</template>
<script lang="ts">
    import {Component, Inject, Vue} from 'vue-property-decorator';
    @Component({})
    export default class Child extends Vue {
        @Inject('name')
        name!: string; // non-null assertion operator
    }
</script>
```

The `vue-property-decorator` also exposes `@Provide` decorator for declaring providers.

Using the `@Provide` decorator, we can make dependencies available in the parent component:

```typescript
//Parent component
export default class ParentComponent extends Vue { 
  @Provide("user-details") userDetails: { name: string } = { name: "Paul" }; 
}
```

Similarly, dependencies can be injected into the child component:

```typescript
//Child component
<script lang="ts">
    import {Component, Inject, Vue} from 'vue-property-decorator';
    @Component({})
    export default class ChildComponent extends Vue {
        @Inject('user-details')
        user!: { name: string };
    }
</script>
```

### Provider hierarchy

The provider hierarchy rule states that if the same provider key is used in multiple providers in the dependency tree of a component, then the provider of the closest parent to the child component will override other providers higher in the hierarchy.

Let’s consider the following snippet for ease of understanding:

```typescript
FatherComponent
├── SonComponent
   ├── GrandsonComponent




//Father component
<script lang="ts">
    import {Component, Vue} from 'vue-property-decorator';
    import SonComponent from '@/components/Son.vue';
    @Component({
        components: {
            SonComponent
        },
        provide: {
            'family-name': 'De Ekongs',
        },
    })
    export default class FatherComponent extends Vue {
    }
</script>
```

In the above snippet, the `family-name` dependency is provided by the `FatherComponent`:

```typescript
//Son component
<script lang="ts">
    import {Component, Vue} from 'vue-property-decorator';
    import GrandsonComponent from '@/components/Grandson.vue';
    @Component({
        components: {
            GrandsonComponent
        },
        provide: {
            'family-name': 'De Royals',
        },
    })
    export default class SonComponent extends Vue {
    }
</script>
```

In the above snippet, the `SonComponent` overrides the `family-name` dependency previously provided by the `FatherComponent`:

```typescript
//Grand son Component
<template>
  <h1> Our family name is {{familyName}}</h1>
</template>
<script lang="ts">
    import {Component, Inject, Vue} from 'vue-property-decorator';
    @Component({})
    export default class Child extends Vue {
        @Inject('family-name')
        familyName!: string; // non-null assertion operator
    }
</script>
```

As you would guess, `De Royals` will be rendered in the template of the `GrandsonComponent`.

In some complex Vue projects, you might avoid overriding dependencies to achieve consistency in the codebase. In such situations, overriding dependencies is seen as a limitation.

Fortunately, JavaScript has provided us with the [ES6 symbols](https://blog.logrocket.com/practical-use-cases-for-javascript-es6-proxies/) as a remedy to the drawback associated with multiple providers with the same keys.

[According to MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol), "Symbols are often used to add unique property keys to an object that won’t collide with keys any other code might add to the object, and which are hidden from any mechanisms other code will typically use to access the object."

In other words, every symbol has a unique identity:

```javascript
Symbol('foo') === Symbol('foo')  // false
```

Instead of using the same string key on the provider and injection sides as we did in our previous code, we can use the ES6 `Symbol`. This will ensure that no dependency gets overridden by another:

```javascript
export const FAMILY = {
    FAMILY_NAME: Symbol('FAMILYNAME'),
};
```

## Advantages to dependency injection

1.  Improves code reusability
2.  Eases the unit testing of applications through mocking/stubbing injected dependencies
3.  Reduces boilerplate code because dependencies are initialized by their injector component
4.  Decouples component logic
5.  Makes it easier to extend the application classes
6.  Enhances the configuration of applications

## Caveats to dependency injection

1.  Dependency injection in Vue does not support constructor injection. This is a major drawback for developers using class-based components because the constructor will not initialize the component class properties
2.  Many compile-time errors are pushed to runtime
3.  With Vue dependency injection, code refactoring can be very tedious
4.  Vue’s dependency injection is not reactive

## Conclusion

In this article, we established a basic understanding of dependency injection in Vue. We walked through the drawbacks associated with multiple providers with the same keys while we also implemented a remedy to the drawback using the ES6 symbols.

---

## Experience your Vue apps exactly how a user does

Debugging Vue.js applications can be difficult, especially when there are dozens, if not hundreds of mutations during a user session. If you’re interested in monitoring and tracking Vue mutations for all of your users in production, [try LogRocket] (https://www2.logrocket.com/vue-performance-monitoring).

[![LogRocket Dashboard Free Trial Banner] (https://files.readme.io/00591d0-687474703a2f2f692e696d6775722e636f6d2f6a3049327856572e706e67.png)](https://www2.logrocket.com/vue-performance-monitoring)

[LogRocket] (https://www2.logrocket.com/vue-performance-monitoring) is like a DVR for web apps, recording literally everything that happens in your Vue apps including network requests, JavaScript errors, performance problems, and much more. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred.

The LogRocket Vuex plugin logs Vuex mutations to the LogRocket console, giving you context around what led to an error, and what state the application was in when an issue occurred.

Modernize how you debug your Vue apps - [Start monitoring for free] (https://www2.logrocket.com/vue-performance-monitoring).