[Vue.js]ライフサイクルを利用してpropsの変更を検知する
親コンポーネントから子コンポーネントへ渡されたpropsの変更を検知して処理を行いたい時の方法です。
やりたいこと
条件に応じてUIパーツの活性・非活性を切り替える
→ 具体例) すでにメールアドレス登録済みであれば、パスワードリセットメール送信のボタンを活性化。メールアドレスが登録されていなければ、非活性にする。
🟡 親コンポーネントでやること
- APIから情報を取得
- 取得した情報を子コンポーネントに渡す
🟡 子コンポーネントでやること
- 親からユーザーの詳細情報を受け取る
- メールアドレスの登録有無を元に、パスワードリセットボタンの活性・非活性を制御する
まずは全体のコード
<template>
<Child
v-bind="userDetail"
/>
</template>
<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>
<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>
<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