※ 当記事は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を使って、正常にデータバインディングが実現できています。
コメント