質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.51%

  • Vue.js

    1117questions

    Vue.jsは、Webアプリケーションのインターフェースを構築するためのオープンソースJavaScriptフレームワークです。

  • Vue CLI

    27questions

親コンポーネントでのデータの変更を子コンポーネントで全て検知したい

解決済

回答 2

投稿

  • 評価
  • クリップ 1
  • VIEW 319

panamax

score 11

VueCLI3を使っています。
親コンポーネントでのデータの変更が子コンポーネントの方で検知されるはずのところが、実際にはされておらず、その原因が分からなかったため質問させていただきます。

まず、親子コンポーネントが以下のような構成で作られています。
質問の趣旨のため、親コンポーネントはmethodsの部分のみ、子コンポーネントはpropsとcomputedの一部分のみの、出来るだけ今回の質問に関係ある部分のソースコードのみを載せます。

<親コンポーネント>

methods: {
        fuga() {
        console.log("とりあえずemit成功したっぽいよ!!");
        if (this.lastEl1) {
          this.lastEl = this.lastEl1;
          console.log("lastEl1のfuga()発動したよ!!");
        }
        if (this.lastEl2) {
          this.lastEl = this.lastEl2;
          console.log("lastEl2のfuga()発動したよ!!");
        }
        if (this.lastEl3) {
          this.lastEl = this.lastEl3;
          console.log("lastEl3のfuga()発動したよ!!");
        }
    }
  }

<子コンポーネント>

  props: {
    lastEl: String,
  },
  computed: {
    processText() {
      console.log("関数processTextが実行されたよ!!");
      if (this.lastEl === "1") {
        処理1
      } else if (this.lastEl === "2") {
        処理2
      } else if (this.lastEl === "3") {
        処理3

       <以下さらに処理が続いていく...>


このソースがどのような動きをするか簡単に説明すると、まず親コンポーネントにて定義したlastElという、"1"~"16"までの値を持つString型のデータがあるイベントによって決まります。

そうすると、子コンポーネントではlastElをpropsに登録しているため、親コンポーネントでlastElの値が変わるとともに、computedに登録した関数processTextが実行されます。(この関数では、lastElの"1"~"16"の値によってそれぞれある処理がなされます)

ここで、親コンポーネントのソースコードではlastElの値を変更するif文が3つあります。それぞれのif文に出てくるlastEl1, lastEl2, lastEl3という値は、lastElの値とは関係なくそれぞれに"1"~"16"の値のいずれかを持っており、それがlastElに代入されることになります。

そして、ここが問題なのですが、if文が3つありlastEl1〜lastEl3の値がそれぞれ異なるならば、計3回lastElの値が変更されたことになり、当然lastElの値の変更を検知した3回分、子コンポーネントの関数processTextが行われるはずです。

しかし、実際に関数processTextが実行されて処理が行われたのは一回だけでした。
これを、lastElの値が変更された回数だけ、適切に子コンポーネントの関数が実行されるようにするにはどうしたらよいでしょうか?

私が試してみた策としては、
以下のように子コンポーネントにthis.$nextTickを使ってみたりしました。
しかし、解決には至りませんでした。

methods: {
        fuga() {
        console.log("とりあえずemit成功したっぽいよ!!");
        this.$nextTick(() => {
          if (this.lastEl1) {
            this.lastEl = this.lastEl1;
            console.log("lastEl1のfuga()発動したよ!!");
          }
        });
        this.$nextTick(() => {
          if (this.lastEl2) {
            this.lastEl = this.lastEl2;
            console.log("lastEl2のfuga()発動したよ!!");
          }
        });
        this.$nextTick(() => {
          if (this.lastEl3) {
            this.lastEl = this.lastEl3;
            console.log("lastEl3のfuga()発動したよ!!");
          }
        });
    },
}

Vue.jsは非同期処理をしているために同じ値(今回の場合はlastEl)を一度に複数回変更してもそれが検知されないのでしょうか?

Vue.jsのライフサイクルなども関係するのかもしれませんが、そこらへんについての知識がなく質問をしました。よろしくお願いします。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

+2

Vue.jsは非同期処理をしているために同じ値(今回の場合はlastEl)を一度に複数回変更してもそれが検知されないのでしょうか?

こちらについては、公式ドキュメントに記載があります。

Vue は 非同期 に DOM 更新を実行します。データ変更が検出されると、Vue はキューをオープンし、同じイベントループで起こる全てのデータ変更をバッファリングします。同じウオッチャが複数回トリガされる場合、キューには一度だけ入ります。

vm.$nextTick()メソッドを使用しても、当該のデータ変更すべてが同じ(次の)イベントループに延期されるだけなので、結果は同じです。

重複した更新がはぶかれるのは、こちらのサンプルで確認できます。
質問のソースコードのように、同じデータを3回変更しますが、watchで指定した関数が実行されるのは1度だけです。
コンソールには最後の変更(changed: null -> 3)だけが表示されます。

親コンポーネントで(if文のたびに)子コンポーネントのメソッドを実行することもできますが、vm.$refsはリアクティブではない(更新されない)ので微妙かもしれません。

いずれにせよ、panamaxさんの目指している実装のゴールに対して、設計からアプローチを変えた方がいいかもしれません。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

+1

computedプロパティは文字通りstateやpropsから「計算される」値を(副作用のない純粋な)関数によって宣言的に定義するもので、その関数がいつ何回呼ばれるかに依存したプログラムは推奨されません。

「変更を検知する」というセマンティクスに合ったwatchプロパティを使うべきです。

そして、ここが問題なのですが、if文が3つありlastEl1〜lastEl3の値がそれぞれ異なるならば、計3回lastElの値が変更されたことになり、当然lastElの値の変更を検知した3回分、子コンポーネントの関数processTextが行われるはずです。
しかし、実際に関数processTextが実行されて処理が行われたのは一回だけでした。

おそらくメソッド(今の場合fuga())の実行が終了するまでは子コンポーネントへのstateの変更を反映しないようになっているのだと思います。lastElの値が同期的に複数回変更されただけなので、子コンポーネントにとっては、最終的な値以外はどうでもいいため、わざわざ3回も自身のcomputedプロパティを計算し直す理由がありません。

これを、lastElの値が変更された回数だけ、適切に子コンポーネントの関数が実行されるようにするにはどうしたらよいでしょうか?

lastElの値が変更された回数だけcomputedプロパティが計算し直されてほしい理由はなんでしょうか?

ちなみにwatchオプションを代わりに使用してみたところやはり1度しかwatchオプションに指定した関数は呼び出されませんでした。親のメソッドで何度stateが変更されても子にとっては最初と最後の値だけみて「1回の変更」とみなされるようです。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/11/28 18:21

    lastEl の値が変更された回数だけ computed プロパティが計算し直されて欲しい理由は、lastElにlastEl1~3の値("1"~"16"のいずれか)が代入されたことを検知するたびに、その値ごとに、指定した処理が行われて欲しかったからです。

    watchオプションでも「1回の変更」とみなされてしまうということで、設計のアプローチを変えてやっていく事にしました。
    最初に私が設計を考えていたときは、lastElという1つのdataの状態をみて処理を行う、というのが楽に思えたのですが、その操作を実現する方法が見つからなかったということは(恐らく実現する方法が完全にないわけではないと思いますが)、そもそもあまり好ましいやり方ではないのかもしれないと思うに至りました。

    色々と勉強になりました。ありがとうございます。

    キャンセル

同じタグがついた質問を見る

  • Vue.js

    1117questions

    Vue.jsは、Webアプリケーションのインターフェースを構築するためのオープンソースJavaScriptフレームワークです。

  • Vue CLI

    27questions