Provide and Inject

in Vue 3

(and why you might not need vuex)

Who Am I?

  • Alex Riviere
  • Senior Frontend Developer at Traina
  • Co-Organizer for Atlanta Vue.js Meetup
  • Co-Host of the Enjoy the Vue Podcast
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

Pros:

Challenging when your app gets larger, and you need data in a deeply nested component.

Cons:

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

Pros:

Cons:

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>

Provide/Inject

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

Provide/Inject

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

Provide/Inject

  • Can be used to pass data between a
    component and its descendants
  • Doesn't rely on a $root component
    containing the right data
  • Doesn't rely on the correct structure
    to find $parent

SHOW ME

HOW ALREADY!

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)
      },
    }
  },
}

Reactive Provide/Inject

Where should I use this?

A good example is compound components where the children components only function inside of their parent components.

When data is implicitly expected to be available.

Where should I use this?

Another good example is using it for a

Global or Local Store

When data is implicitly expected to be available.

Where should I use this?

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

The following example was blatantly stolen from

Cassidy Williams' React Hooks Workshop

 

Follow Cassidy, because she is awesome:
twitter: cassidoo

github: cassidoo

twitch: cassidoo

An Accordion

  • [data-accordion]
  • [data-panel-section]
  • [data-panel-title]
  • [data-panel-content]

Let's break this down

AccordionWrapper

AccordionSection

AccordionTitle

AccordionContent

Bring it Home - All Together

What Else?

🔥You may not need Vuex🔥

  • Use Provide and wrap your app, views,
    or local components in a Data Layer
  • Will only load data when you need it
  • Might solve your issue with having a giant vuex store load on every page

The

Mildly Over-engineered

Counter

Component

Introducing:

The Counter Component

Our "CounterProvide" Data Layer Component

Our "CounterDisplay" Component

Our "CounterButtons" Update Component

The Fully Realized Counter Component

What have we learned?

  • There are many ways of passing data between components
    • Props/Emit
    • Global Store (Vuex)
    • $root/$parent
    • Provide/Inject
  • Provide/Inject can be used to implicitly pass data between nested components.
  • Provide/Inject is fantastic for Compound Components
  • You can make your own Data Layers to provide data to your App, Route, or local components!

Questions?