Vue – Provide & Inject

If you visit my blog regularly, you probably know, that I really like Vuex in Vue. It’s amazing tool, that allows us to solve a lot of issues with data flow, it also allows us to organize and clean code. But sometimes, Vuex is too big tool to use. For small apps, small pages, we probably do not need such “engine”. I think even in such cases, we should use it, because our app can get much bigger in the future, but for many people, it’s not necessary and maybe redundant. This post is about Provide & Inject pattern in Vue – it’s similar to Vuex in many times, but do not require any additional tools, it’s build-in into Vue core.

List example

I will describe the whole thing on an example with few components. One parent and several children, nested. Let’s say we have such structure of components in our app:App

- App
-- Header
-- List
--- ListElement
---- ListElementButtons
-- Footer

For example, posts, comments, list of users etc. We can multiply examples, it’s very common situation in many apps. Let’s say we load all required data (from API, files, or just from hardcoded array) in our App component. It’s also common pattern, because probably we will not render List and many components inside without required data. After fetch, we have all things and can use props to put our data into list, then into element. It’s clear and very simple. We can also emit events from nested component to parents and call methods, it’s also clear and very simple.

Props & events chain

But what if we want to call something in App from ListElement or ListElementButtons? For example, each list element can be deleted, and we want to display modal/alert before with information and confirmation request. It’s very common situation, but in such case, may be problematic, because we have to create event “chain”. ListElementButtons has to emit “delete” event to ListElement. Then ListElement has to emit “delete” event to List and finally, List has to emit “delete” event to App, which will modify data and will send it to Header (or maybe new component). It’s nothing special, but messy and required a lot of redundant code.

Solution: provide & inject

Of course, we can solve that using Vuex – dispatch action to set flag, observe this flag on App (or even Header) component and do what we need. But using Vuex for such small thing may be weird. Vue allows us to use simpler option – Provide & Inject. We can use “provide” method on our parent component to… provide data and methods in children – all children, which will use “inject” method. Nesting level doesn’t matter here and we do not have to create any chain, like on props, to send data deeper and deeper. Let’s add some code to App component to make it clear:

// App component

data () {
    return {
        someText: 'Test'
    }
}

methods: {
    showModal (item) {
        // logic here
    }
}

provide () {
    return {
        appSomeText: this.someText,
        appShowModal: this.showModal
    }
}

I use some example data, to show, what can we provide from parent – almost everything. It’s no problem to provide data elements, computed properties or methods. We only have to list them on object that provide returns. There is something imporant: we can use “provide” just like object, but in such case, we will be not able to use component properties:

provide: {
     appSomeText: 'some text', // it's ok
     appSomeOtherText: this.someText // it will cause error
     appShowModal: this.showModal // also will cause error
 }

Ok, we have prepared parent, now it’s time to use it in nested components. It’s very simple and similar to using props:

// ListElementButtons

inject: ['appSomeText', 'appShowModal]

methods: {
    clickDelete () {
        this.showModal(this.item)
    }
}

And… it’ts all. It will work as expected, we can display appSomeText in our ListElementButtons component and call method from App component without any issues. As you can see, we do not have to pass anything down and emit enything up in such case. We removed a lot of redundant code and make all the thing much cleaner, without any additional packages like Vuex, without tons of files (state, actions, mutations, getters). Simple and clean.

Drawbacks

Of course nothing is ideal and also Provide & Inject patter is not ideal thing. It’s ok for small apps, when we do not use Vuex – because of simplicity of that solution. But in bigger apps or more complicated hierarchy, it can make code much harder to understand and read. Of course, there will be less code than using props/emits on all components, but fresh person will not know, what and excatly where has been used – such person has to search for injects and checks dependencies. It can take some time. Also mentioned dependencies: in such case, App has to provide such method, so both components (App and ListElementButtons) are dependent on each other. With Vuex, we can work on flags and make everything more flexible.