Dependency Injection

in Vue.js

Who Am I?

  • Alex Riviere
  • Design Engineer for NexTraq
  • Co-Organizer for Atlanta Vue.js Meetup
WARNING

Advanced Vue.js Concepts Ahead

May Cause:

  • Confusion
  • Sense of Wonder
  • Smug satisfaction of knowing more than others.

Methods of Passing State Between Components

  • Props/Emit
  • External Store (Vuex)
  • $root/$parent (Not Advised)
  • Provide/Inject

Props Down, Events Up

<parent-component>
<child-component>
<grand-child-component />

$props

$props

$emit

$emit

</child-component>
</parent-component>

Props Down, Events Up

  • Explicitly Defined Props and Events
  • Creates reusable components
  • Doesn't rely on outside data structures
  • Easily testable

Global Store

<fox-component />
<whale-component />
<unicorn-component />

 Vuex $store

state

state

state

mutation

mutation

mutation

Global Store

  • Useful for large applications with shared state
  • Can be used along with props/$emit
  • Implicitly uses $store
  • Relies on external dependency
  • Not ideal for publishing modules

I am $root

<walking-tree-component>
<raccoon-component>
<laser-pew-pew-component />

$root.bitOfData

</raccoon-component>
</walking-tree-component>

I am $root?

<walking-tree-component>
<raccoon-component>
<laser-pew-pew-component />

$root.bitOfData?

</raccoon-component>
</walking-tree-component>
<galaxy-guardians>
</galaxy-guardians>

$parent Knows Best

<parent-component>
<child-component>
<grand-child-component />

$parent.knowledge

$parent.$parent.knowledge

(Has a bit of data called knowledge)

</child-component>
</parent-component>

$parent Knows Best ?

<parent-component>
<grand-child-component />

$parent.$parent.knowledge?

</parent-component>

Dependency Injection

<parent-component>
<grand-child-component />
provide
inject
<child-component>
<grand-parent-component>
</child-component>
</parent-component>
</grand-parent-component>

Dependency Injection

<providing-component>
<injecting-component />
<child-component>
<child-component>
<root-component>
_provided.bit_of_data
inject:['bit_of_data']
_provided.bit_of_data?
_provided.bit_of_data?
this.bit_of_data
</child-component>
</child-component>
</providing-component>
</root-component>

SHOW ME

HOW ALREADY!

<div>
  <p>
    This is the count:
  </p>
  <p>{{count}}</p>
  <slot />
  <button 
    @click="count++"
    class="button">
    Increment!
  </button>
</div>
SimpleProvide.vue
export default {
  name: 'simple-provide',
  data(){
    return {
      count: 1,
    };
  },
  provide(){
    return {
      'count': this.count
    };
  },
}
Template
Script
<div>
  <p>
    This is the 
    injected count:
  </p>
  <p>
    {{count}}
  </p>
</div>
SimpleInject.vue
export default {
  name: "simple-inject",
  inject: ['count'],
}
Template
Script

Simple Demo

How do we make this Reactive?

RxJS!

NO!!

<div>
  <p>
    This is the 
    parent text:
  </p>
  <p>{{text}}</p>
  <input 
    type="text"
    v-model="text" />
  <slot />
</div>
ReactiveProvide.vue
export default {
  data() {
    return {
      text: "Hello World",
    }
  },
  provide() {
    return {
      getText: ()=>this.text,
      setText: v=>this.text=v,
    }
  },
}
Template
Script
<div>
  <p>
    Injected text:
  </p>
  <p>{{ text }}</p>
  <input 
    type="text"
    v-model="text" />
</div>
ReactiveInject.vue
export default {
  name:"reactive-inject",
  inject: [
    "getText", 
    "setText",
  ],
  computed: {
    text: {
      get() {
        return this.getText()
      },
      set(d) {
        this.setText(d)
      },
    }
  },
}
Template
Script

Reactive Demo

ReactiveProvide.vue
import {ref, provide} from 'vue';

export default {
  setup(){
    const text = ref('Hello World');
    provide('text', text);
    return {text}
  },
}
Vue 2 + 3
Vue 3 Composition API
export default {
  data() {
    return {
      text: "Hello World",
    }
  },
  provide() {
    return {
      getText: ()=>this.text,
      setText: v=>this.text=v,
    }
  },
}
ReactiveInject.vue
import {inject} from 'vue'
export default {
  name:"reactive-inject",
  setup(){
    const text = inject('text');
    return {text};
  },
}
Vue 2 + 3
Vue 3 Composition API
export default {
  name:"reactive-inject",
  inject: [
    "getText", 
    "setText",
  ],
  computed: {
    text: {
      get() {
        return this.getText()
      },
      set(d) {
        this.setText(d)
      },
    }
  },
}

Helpful Packages

vue-reactive-provide

Plugin/Mixin wrapping Vue's static 'provide/inject' feature allowing to easily pass reactive data to children

https://github.com/LinusBorg/vue-reactive-provide

convext

A Context-API like interface for Vue.js

https://github.com/fimion/convext

(I had to put the V somewhere.)

Where should I use this?

When data is implicitly expected to be available. A good example is compound components where the children components only function inside of their parent components.

Let's look at an example using an accordion and splitting it up into multiple components.

Demos!

This example was blatantly stolen from Cassidy Williams' React Hooks Workshop

 

Follow Cassidy, because she is awesome:
twitter: cassidoo

github: cassidoo

twitch: cassidoo

Questions?