※ 当記事はvue@2.7.4で動作確認を行なっています。

provide/injectの仕組み

従来のVue.jsでは、コンポーネント間のデータバインディングにはPropsを用いてきましたが、大規模なアプリケーションでは大量のデータバインディングが発生し、コードのメンテナンスが大変でした。

さらに親コンポーネントのデータを孫コンポーネントが使いたいならば、まず親から子にPropsでデータを渡し、さらに子から孫へとPropsで(もしくは別のdataで)データを渡す必要がありました。

そこでprovide/injectという仕組みが用意され、propsを使わなくてもコンポーネント間でデータバインディングできるようになりました。

具体的には、アプリケーション内のすべてのコンポーネントがアクセスできる値をprovideで設定し、provideされている値を受け取りたいコンポーネントは、injectを使用すればデータを受け取れます。

下図で掘り下げると、親コンポーネントで子コンポーネントに渡したいデータをprovideで定義します。

親コンポーネントのデータを使いたい子または孫コンポーネントは、injectでprovideのkeyを定義することでデータを受け取れる仕組みになっています。

この仕組みを使うことで、親コンポーネントのデータを使いたい孫コンポーネントもinjectを定義するだけでデータを受け取ることができるのです。

構文

例えば、親コンポーネントで定義されたusers(配列)を子コンポーネントに渡したいなら、下記のように定義します。

// 親コンポーネント
const Parent = {
  data: () => ({
    users: []
  }),
  provide() {
    return {
      users: this.users
    }
  },
  // ...
}

// データを受け取りたい子コンポーネント
const Child = {
  inject: ['users'],
  created () {
    console.log(this.users) // => "[]"
  }
  // ...
}

サンプルコード

上記の構文に倣って、親コンポーネントでusersオブジェクトのデータ変更やデータの追加を発生させて、親コンポーネントのデータ変更が子コンポーネント反映されているか確認します。

■ 親コンポーネント(Parent.vue)

<template>
  <v-container>
	  <ProvideInjectChild></ProvideInjectChild>
	  <v-btn @click="add()">ユーザー追加</v-btn>
	  <div>{{ users }}</div>
  </v-container>
</template>


<script>
import ProvideInjectChild from './ProvideInjectChild.vue'
export default {
  components: {
    ProvideInjectChild,
  },
  data: () => ({
    users: [
      { name: "Luffy" },
      { name: "Sanji" },
      { name: "Zoro" },
    ]
  }),
  provide() {
    return {
      users: this.users
    }
  },
  mounted() {
    setTimeout(() => {
      this.users[0].name = "Nami";
    }, 5000);
  },
  methods: {
    add() {
      this.users.push(
        { name: "Kid" }
      )
    }
  },
}
</script>

■ 子コンポーネント(Child.vue)

<template>
  <div>
    {{ users.length }}
    <div v-for="(user, index) in users" :key="index">
      {{ user.name }}
    </div>
  </div>
</template>


<script>
export default {
  inject: ["users"],
}
</script>

実際の動作

数秒後に”Luffy”の名前が”Nami”に変わり、ユーザー追加ボタンを押すと「Kid」が追加されました!

provide/injectを使って、正常にデータバインディングが実現できています。

カテゴリー: Vue.js