どういうこと…?
Nuxt使ってる時点で、そのリアクタビリティにあぐらをかいている私たちは、時折算出プロパティ(computed)が一向に更新されない事態に出くわし、途方にくれることがあります。
今回もそうでした。firestoreで既存コメントのアップデートをリッスンしていた私は、返り値を配列にぶっこんでも更新されないことに気が付きました。
ちょっと雑に再現しますが、こんな感じ。
export default {
data() {
return {
comments: [],
},
},
computed: {
filteredComments() {
return this.comments.filter(c => !c.hidden);
}
},
created() {
this.comments = await getComments();
firestore.collection('activities').doc('comment:update').onSnapshot({
next: (snapshot) => {
const data = snapshot.data();
const index = _.findIndex(this.comments, c => c.id === data.id);
this.comments[index] = data;
}
});
}
}
「またか…」と溜息をついた私は、本腰入れて解決法を探りました。
変更が検知されてないね
で、わかったことは、Nuxt(Vue)側で、この変更を検知してないということでした。watch
入れて見てみようが、this.filteredComments
にセッターを定義して、改めて明示的に値をセットしようがダメ。
ただ、プロパティを直で入れてみると検知されます。だから、こんな感じにすれば大丈夫。
this.comments[index].hidden = data.hidden;
でも、これだとプロパティが変わった時とかいちいち手を入れなければいけなくてバグの温床技術負債となること待ったなしなので、ひとまずこうしてみた。動いた。
Object.keys(data).map(key => {
this.comments[index][key] = data[key];
})
でも、これ、ちょっと信じがたいほどダサい。2021年に書くコードではない。そこで、きちんとドキュメントに当たりはじめました。
解決!
基本、ここに書いてあるとおりでした。
Vueのドキュメントに詳細があって、要するに、**「インデックスと一緒にアイテムを直接セットする場合」**は更新がリアクティブに検知されないのですね。
なので、$set
を使って、このように実装してやりましょう。
this.$set(this.comments, index, data);
見事解決です。みなさん、良いVueライフを。