❤️‍🔥

[Vue.js]ライフサイクルを利用してpropsの変更を検知する

2022/09/21に公開

親コンポーネントから子コンポーネントへ渡されたpropsの変更を検知して処理を行いたい時の方法です。

やりたいこと

条件に応じてUIパーツの活性・非活性を切り替える
→ 具体例) すでにメールアドレス登録済みであれば、パスワードリセットメール送信のボタンを活性化。メールアドレスが登録されていなければ、非活性にする。

🟡 親コンポーネントでやること

  1. APIから情報を取得
  2. 取得した情報を子コンポーネントに渡す

🟡 子コンポーネントでやること

  1. 親からユーザーの詳細情報を受け取る
  2. メールアドレスの登録有無を元に、パスワードリセットボタンの活性・非活性を制御する

まずは全体のコード

Parent.vue
<template>
 <Child
  v-bind="userDetail"
  />
</template>
Parent.vue
<script>
import Child from './Child.vue'
import { API, graphqlOperation } from "aws-amplify";
import * as queries from "@/graphql/queries";

export default {
 components: {
  Child
 },
 data(){
  return{
   // 子コンポーネントにpropsで渡す
   userDetail: {}
  }
 },
 // 初期描画でユーザーIDを元にユーザー情報を取得
 created(){
   getUserDetails(){
     API.graphql(graphqlOperation(queries.getUser, {
        input: {
          user_id: this.$route.params.id
          }
       })
      ).then((response) => {
       let userInfo = response.data.getUser;
    
       this.userDetail = {
         userId: userInfo.id,
	 userName: userInfo.name,
	 userMail: userInfo.mail,
      }
     }
     ).catch((err) => {
         console.log(err);
    })
   }
  }
}
</script>
Child.vue
<template>
      <Table>
        <tbody>
            <tr>
              <th>表示名</th>
              <td>{{ userName }}</td>
            </tr>
            <tr>
              <th>ユーザーID</th>
              <td>{{ userId }}</td>
            </tr>
            <tr>
              <th>メールアドレス</th>
              <td>
                {{ userMail }}
                <Button
                  :isDiabled="isUserMail"
                  label="パスワードリセット"
                  @click="onClickReset"
                />
              </td>
            </tr>
          </template>
        </tbody>
      </Table>
</template>
Child.vue
<script>
import Child from './Child.vue'
import { API, graphqlOperation } from "aws-amplify";
import * as queries from "@/graphql/queries";

export default {
  // 1)  propsに受け取る値を設定して、親からユーザーの詳細情報を受け取る
  props:{
   userName: {
     type: String,
   },
   userId: {
     type: String,
   },
   userMail: {
     type: String,
   },
 }
 data(){
   return {
    isUserMail: false,
   }
 }
 //  2) メールアドレスの登録有無を元に、パスワードリセットボタンの活性・非活性を制御する
 // この時点では、親の初期値が渡ってくるためAPIで取得したユーザー情報は渡ってこない
 created(){
   console.log(this.$props.userMail); // undefined
 },
 // データが変更された時 = propsの値が変更されたときに呼び出される
 beforeUpdate(){
   // メールアドレスが空だった場合
   if(!this.userMail){
     // ボタンを非活性にする
    this.isUserMail = true;
   }
 }
}
</script>

解説

上記のようにvueのライフサイクルメソッドであるbeforeUpdateを使用することで
propsの変更を検知して、それをDOMに反映させることができます

beforeUpdateメソッドとは

細かいライフサイクルについての説明は省きますが、(詳しく知りたい方は
beforeUpdateはデータが更新されDOMに適用される前に実行されます。
※名前がややこしいですがUpdateはDOMの更新を表すため、DOM更新前 = データは更新されている

親コンポーネント内でAPIからデータを取得したタイミングで
データが更新され、子コンポーネントでbeforeUpdateが発火されるようになります。

ちなみに親コンポーネントのdataプロパティ内で値が決まっている場合は
わざわざbeforeUpdateを使う必要はなくcreatedで同処理を書けば問題ないですが、
今回のようにAPIで取得したデータを元に画面制御する場合はcreatedではdataプロパティの初期値しか
取ってこれない為下記のようにundefinedという結果になります。

 created(){
   console.log(this.$props.userMail); // undefined
 },
 beforeUpdate(){
   // メールアドレスが空だった場合
   if(!this.userMail){
     // ボタンを非活性にする
    this.isUserMail = true;
   }
 }

watchの方が理想的?

今回はライフサイクルメソッドを用いたpropsの変更検知処理を書きましたが、
beforeUpdateはどのデータが更新されても呼び出されます。
特定のデータが更新された時に処理を実行したい場合はwatchを使うことで実現できます。

複数データの変更を検知するにはbeforeUpdateが良さそうですね。

Discussion