Vue 3 — Provide and Inject

John Au-Yeung - Jan 25 '21 - - Dev Community

Check out my books on Amazon at https://www.amazon.com/John-Au-Yeung/e/B08FT5NT62

Subscribe to my email list now at http://jauyeung.net/subscribe/

Vue 3 is in beta and it’s subject to change.

Vue 3 is the up and coming version of Vue front end framework.

It builds on the popularity and ease of use of Vue 2.

In this article, we’ll look at providing and injecting data with Vue 3.

Provide and Inject

To make passing data easier between deeply nested components, we can bypass props and use the provide property to expose some data.

And then we can use the inject property to get the data.

For example, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      <blog-post></blog-post>
    </div>
    <script>
      const app = Vue.createApp({});

      app.component("blog-post", {
        data() {
          return {
            post: { title: "hello world" }
          };
        },
        provide: {
          author: "james"
        },
        template: `
          <div>
            {{ post.title }}
            <blog-post-statistics></blog-post-statistics>
          </div>
        `
      });

      app.component("blog-post-statistics", {
        inject: ["author"],
        template: `
          <p>{{author}}</p>
        `,
        created() {
          console.log(this.author);
        }
      });

      app.mount("#app");
    </script>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

We have the blog-post and blog-post-statistics components.

In the blog-post component, we have the provide property with the author property.

This is the data that we’re providing to child components.

Therefore, in the blog-post-statistics component, we have the inject property with an array of the property names that are in provide that we want to get.

So author in blog-post-statistics would be author in the template or this.author in component methods.

However, this won’t work with Vue instance properties.

So if we have:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      <blog-post></blog-post>
    </div>
    <script>
      const app = Vue.createApp({});

      app.component("blog-post", {
        data() {
          return {
            post: { title: "hello world", author: "james" }
          };
        },
        provide: {
          post: this.post
        },
        template: `
          <div>
            {{ post.title }}
            <blog-post-statistics></blog-post-statistics>
          </div>
        `
      });

      app.component("blog-post-statistics", {
        inject: ["post"],
        template: `
          <p>{{post.author}}</p>
        `,
        created() {
          console.log(this.post.author);
        }
      });

      app.mount("#app");
    </script>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

Then nothing will be displayed.

To fix this, we got to pass in the property that has the value to provide and change provide to a function:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      <blog-post></blog-post>
    </div>
    <script>
      const app = Vue.createApp({});

      app.component("blog-post", {
        data() {
          return {
            post: { title: "hello world", author: "james" }
          };
        },
        provide() {
          return {
            author: this.post.author
          };
        },
        template: `
          <div>
            {{ post.title }}
            <blog-post-statistics></blog-post-statistics>
          </div>
        `
      });

      app.component("blog-post-statistics", {
        inject: ["author"],
        template: `
          <p>{{author}}</p>
        `,
        created() {
          console.log(this.author);
        }
      });

      app.mount("#app");
    </script>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

Now the provide is a method of the blog-post component instead of a property.

And we return what we want to provide to child components.

Working with Reactivity

Changes with reactive properties in the parent won’t be reflected in child components when we update them.

This is a problem that can be fixed by making the reactive property a computed property.

For instance, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      <counter></counter>
    </div>
    <script>
      const app = Vue.createApp({});

      app.component("counter", {
        data() {
          return {
            count: 0
          };
        },
        provide() {
          return { count: Vue.computed(() => this.count) };
        },
        template: `
          <div>
            <button @click='count++'>increment</button>
            <count-display></count-display>
          </div>
        `
      });

      app.component("count-display", {
        inject: ["count"],
        template: `
          <p>{{count.value}}</p>
        `
      });

      app.mount("#app");
    </script>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

We have a counter component with a provide method that returns an object with the count property.

Its value is a computed property with this.count returned in the callback.

This way, the latest value will be propagated to the child component.

Then in the count-display component, we can get the value with the value property.

Conclusion

Provide and inject gives is a way to pass data to deeply nested components without using props.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .