備忘録を兼ねて
問題点
Vuexfireのドキュメンテーションに以下の記述があります。
Any document bound by Vuexfire will retain it's id in the database as a non-enumerable, read-only property.
bindしたデータをそのままviewから呼び出すのであれば、enumerable属性の真偽に関わらずdata.id
でidプロパティにアクセスできるので問題が生じません。しかし、Object.entries等にObjectを渡した際は検出してくれません。
const db = firebaseApp.firestore();
state = { data: null } // -> { value: 'hoge', id: 'fuga' }
// idはenumerable: falseとして保存されている
actions = {
bindData: firestoreAction(async ({ bindFirestoreRef }) => {
return bindFirestoreRef('data', db.collection('hoge'));
}),
}
for (let [key, value] of Object.entries(this.$store.state.data)) {
console.log(`${key}: ${value}`); // -> { value: 'hoge' }
}
この仕様は、例えばObjectをcomposition-API等に通した際などに問題となり得ます。
setup(props, context) {
const data = ref(context.root.$store.state.data);
console.log(data.value) // -> { value: 'hoge' }
}
解決法
さて、VuexfireのAPIReferenceに次の記述があります
options can contain
serialize
: a function to provide a custom serialization strategy when a document from firebase is set on the Vue instance. This allows to customize the id key, to transform data, etc.
さらにVuefireのReferenceを辿ると次のように記述されています
options.serialize
: The function receives a DocumentSnapshot
as its first argument and is expected to return a plain object to be set on the Vue Instance. Here is the default function that is used when no override is provided:
これに従ってserialize
関数を定義し、callbackとしてbindFirestoreRef
に渡すとenumerable
属性がtrue
になってくれます。
const serialize = (snapshot: firestore.DocumentSnapshot) => {
return Object.defineProperty(snapshot.data(), 'id', {
value: snapshot.id,
enumerable: true,
});
};
actions = {
bindData: firestoreAction(async ({ bindFirestoreRef }) => {
return bindFirestoreRef('data', db.collection('hoge'), { serialize });
})
}
setup(props, context) {
const data = ref(context.root.$store.state.data);
console.log(data.value) // -> { value: 'hoge', id: 'fuga' }
}