Help us understand the problem. What is going on with this article?

vuexfireでbindFirestoreRefすると、idのenumerable属性がfalseになる問題

備忘録を兼ねて

問題点

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を渡した際は検出してくれません。

vuex.js
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'));
  }),
}
index.js
for (let [key, value] of Object.entries(this.$store.state.data)) {
  console.log(`${key}: ${value}`); // -> { value: 'hoge' }
}

この仕様は、例えばObjectをcomposition-API等に通した際などに問題となり得ます。

main.vue
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になってくれます。

vuex.js
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 });
  })
}
main.vue
setup(props, context) {
  const data = ref(context.root.$store.state.data);
  console.log(data.value) // -> { value: 'hoge', id: 'fuga' }
}

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした