2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Vue2系でProps地獄をやめてProvide/Injectをつかう

Posted at

はじめに

備忘録として整理しました。

ハンマーを持つと全てが釘に見えるので、引き出し増やす程度に見ていただければ幸いです。
後述するソースコードはvue2.6.14で動作確認しています。

Vue3のProvide / Inject

以下公式より引用

通常、親コンポーネントから子コンポーネントにデータを渡すとき、props を使います。深くネストされたいくつかのコンポーネントがあり、深い階層にあるコンポーネントが浅い階層にあるコンポーネントの何かしらのデータのみを必要としている構造を想像してください。この場合でも、鎖のように繋ったコンポーネント全体にプロパティを渡す必要がありますが、時にそれは面倒になります。

そのような場合は、provide と inject のペアを利用できます。コンポーネント階層の深さに関係なく、親コンポーネントは、そのすべての子階層へ依存関係を提供するプロバイダとして機能することができます。この機能は 2 つの機能からなります: 親コンポーネントは、データを提供するためのオプション provide を持ち、子コンポーネントはそのデータを利用するためのオプション inject を持っています。
image.png

Vue2系での書き方

Vue3からの機能のように見えますが、実はVue2系でも使うことができます。
そのまま使うと親コンポーネントで更新した値が子コンポーネントに反映されません。
リアクティブではないためです。

リアクティブに扱う方法1 インスタンスをprovideで渡してしまう

こちらの記事では親のインスタンスごと子供に渡しているのですが、記載の通りそこそこなんでもできてしまって注意が必要です。
もし孫から親世代に変更したい場合はインスタンスごと渡しているので直接、もしくはメソッドから変更できます。
なんでもできてしまうので、方法2の方がおすすめです。

リアクティブに扱う方法2 Object.definePropertyをprovideする

調べていたら見つけた動画です。英語ですが今回のやりたいことの解説をそのまましてくれています。

方法1と2のサンプルコード

せっかくなので、孫の変更を親にできないかためしてみました。
あんまりやると無法地帯になりそうですが、propsとemitが連鎖し続けるくらいならこっちのほうがわかりやすい、、、気がします。

ProviderCompを親世代、
KoCompを子世代、
MagoCompを孫世代としてます

ProviderComp.vue
<template>
<div>
  <pre>oya: {{value}} | {{value2}}</pre>
  <div>
    <button @click="countup">Count-up</button>
    <hr>
    <KoComp />
  </div>
</div>
</template>

<script>
import KoComp from "./KoComp";
export default {
  data() {
    return {
      value: 0,
      value2: 0,
    };
  },
  components: { KoComp },
  provide() {
    const provideValue = {}
    Object.defineProperty(provideValue, 'value2',{
      enumerable:true,
      get:()=>this.value2,
      set:(val)=>{this.value2=val} // injectした子孫から影響をうけつけるため。渡すだけならset不要
    })
    return {
      "oyaVM": this, // 方法1
      provideValue   // 方法2
    };
  },
  methods: {
    countup() {
      this.value++;
    },
  },
}
</script>
KoComp.vue
<template>
<div>
  <p> ko</p>
  <hr>
  <MagoComp />
</div>
</template>

<script>
import MagoComp from "./MagoComp";
export default {
  components: { MagoComp }
}
</script>

MagoComp.vue
<template>
<div>
  <p> mago {{oyaVM.value}} | {{provideValue}}</p>
  <button @click="countup">Count-up</button>
  <button @click="countup2">Count-up2</button>
  <hr>
</div>
</template>

<script>
export default {
  inject: ["oyaVM", "provideValue"], // 方法1も2もどちらも

  methods: {
    countup() {
      this.oyaVM.value++; // 方法1
    },
    countup2() {
      this.provideValue.value2++ // 方法2
    },
  },
}
</script>

説明用画像うごかした画像

おわりに

コンポーネントを細かく分解していると階層が深くなって、だんだんPropsの多重バケツリレーが起きてしまいますがそれから解放してくれる便利な機能ですね。

ただデメリットとしては、どうデータがやり取りされているか、流れが追いにくいかもです。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?