By design, the provide
and inject
features of Vue are not reactive, even though many people (myself included!) wish they were.
That’s not a huge problem for us though since there is a way to get around this:
If you create a reactive object using the data()
function or Vue.observable
, it will still be reactive when passed down using provide
and inject
.
It would look like this if you’re using the data function:
export default { provide() { return { reactive: this.reactive }; }, data() { return { reactive: { value: "Hello there" } }; } };
Now when reactive
is injected into another component, it will behave like any other prop and changes to reactive
will trigger updates in your Vue application!
In this article we’ll cover:
data()
function is a great solution to this problemThis should only be used in special circumstances. This technique should not be used as a replacement for props and events. If all you need is a basic way to pass data around, props and events are great, simple, and everyone else who reads your code will understand what’s going on.
So when should you use it?
We’ll cover that a little later on, but first, let’s take a look at three different ways we can make provide and inject reactive.
The first technique is to make the object reactive by initializing it in our data()
function:
export default { provide() { return { reactive: this.reactive }; }, data() { return { reactive: { value: "Hello there" } }; } };
Any object that is initialized here will be made reactive by Vue. Once it is reactive, it is reactive no matter where it gets used.
You can even pass the object to some random JavaScript outside of your Vue app, and any changes made to the reactive object will trigger updates inside of your Vue app.
There is a second way we can make our object reactive, something that’s fairly new to Vue.
Vue 2.6 introduced the observable
function that lets us create our own reactive objects:
import Vue from 'vue'; const state = Vue.observable({ message: "Hello!" });
In fact, this is the same function that Vue uses internally with the data()
function, but here it is exposed to us so that we can use it wherever we want.
In Vue 3, this function will be renamed to reactive
with the addition of the Composition API.
Let’s rewrite the previous example to use Vue.observable
:
import Vue from 'vue'; const reactive = Vue.observable({ value: 'Hello there' }); export default { provide() { return { reactive: reactive, }; }, };
This function gives us a lot more flexibility in how we create our reactive objects since we are no longer reliant on the data
function.
However, in most cases, you’ll be using the previous method because the state that you’re providing will typically already be initialized by the data()
function. But this is a great tool to add to your toolbox in case you ever find that the data()
function just doesn’t quite do what you need it to.
The final method we’ll cover is not a native Vue feature, but a mixin created by Vue core team member Thorsten Lünborg.
LĂĽnborg, who is a member of the Vue core team, has created the vue-reactive-provide
mixin, which you can find on Github.
It’s a super easy way to make provide/inject reactive if you don’t like the other options, and there are two main ways you can use it.
If you want it to feel similar to the native provide
, you just have to install it as a plugin:
import Vue from 'vue'; import ReactiveProvide from 'vue-reactive-provide'; Vue.use(ReactiveProvide);
You would add this to your main.js
file if you’re using Vue CLI.
Then in the component where you want to provide the value, you would use reactiveProvide
instead:
export default { reactiveProvide: { name: 'injectedName', include: ['reactive'], } data() { return { reactive: 'hello', }; }, };
You need to give your reactive object a name
so we know what value to inject in the child. Then you can include any number of fields that you want using the include
array.
To inject this reactive object into a component you would inject it like normal, using the name
value you set earlier:
export default { inject: ['injectedName'] };
Using it as a mixin is nearly the same process, but you don’t need to register it as a plugin first:
import { ReactiveProvideMixin } from 'vue-reactive-provide' export default { mixins: [ ReactiveProvideMixin({ name: 'injectedName', include: ['reactive'], }) ], data() { return { reactive: 'hello', }; }, };
We use the ReactiveProvideMixin
function to dynamically create a mixin. The mixin will provide the included value in a reactive way for us.
To inject the value, we use the exact method as before:
export default { inject: ['injectedName'] };
In general, you should try to avoid using provide/inject, and instead pass data around using props and events. That will get you where you need to go most of the time, and you avoid adding unnecessary complexity and keep your code understandable.
However, there are a few specific questions you can ask yourself when deciding whether or not you should use this feature:
Your use case doesn’t have to pass all of these rules, but it should fit at least one or two of them.
For example, let’s imagine you were working on an app that had fairly straightforward state. Vuex would be overkill, so you’ve decided not to use it.
Each page loads the user’s information in a user
object, and that object is used in all sorts of places all over the app. Displaying the user’s name, their email address, and other information like that is not limited to a specific part in the application.
To avoid passing this data as prop through every single component in our app, we provide
it at the top-level component, so any component that needs it can inject
the user
object and gain access to it directly.
Now, this might seem to violate our fourth rule, but it hits #1 and #3 right on the head. So this ends up being a great solution.
Before we wrap up, there’s one other thing that you should be sure to avoid when using this technique.
When you use a reactive injection like this, a common mistake is to treat it as a two-way binding, where the value can be changed by the component providing it, as well as the one injecting it.
But this is a horrible idea? Only the component that is providing the value should ever be allowed to modify it.
This is for two main reasons:
As we saw, it is possible to make provide and inject reactive, and it doesn’t take too much effort. There are three different ways to do it, so you’ll be able to find something that fits your project.
This technique is really useful, but as I mentioned, it can also make your code unnecessarily complicated. It’s best to stick with regular props and events if that will work for you.
If props and events aren’t working well, we went through a few different questions you can ask yourself to determine if this technique is right for your use case.
Lastly, we covered what two-way data binding is and why you should avoid it.
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.
LogRocket is like a DVR for web and mobile 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.
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowLearn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.
Bypass anti-bot measures in Node.js with curl-impersonate. Learn how it mimics browsers to overcome bot detection for web scraping.
Handle frontend data discrepancies with eventual consistency using WebSockets, Docker Compose, and practical code examples.
Efficient initializing is crucial to smooth-running websites. One way to optimize that process is through lazy initialization in Rust 1.80.