nuxtでfirestoreとvuexのstoreを組み合わせるとうまく保存できないのでラッパーにした話

こんにちは。[virapture株式会社](https://virapture.com/)の[もぐめっと](https://twitter.com/mogmet)です。
![mogmet.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/42234/708f725b-637e-3e3c-5c24-b8d1c16ac48d.jpeg)
こんな格好してるので前世はきっと狛犬でした。しらんけど。

今回はnuxtとvuexのstoreとfirestoreの組み合わせのちょっとしたtipsを紹介します。

## どんなときにエラーになるか

vuexのstoreってプリミティブな型（stringとかintとかDateとか基礎的なやつ）で保存しないと、こんなエラーはいちゃうんですよね。

```
vue.runtime.esm.js?2b0e:2107 Uncaught (in promise) RangeError: Maximum call stack size exceeded
    at Function.[Symbol.hasInstance] (<anonymous>)
    at _traverse (vue.runtime.esm.js?2b0e:2107)
    at _traverse (vue.runtime.esm.js?2b0e:2123)
    at _traverse (vue.runtime.esm.js?2b0e:2123)
    at _traverse (vue.runtime.esm.js?2b0e:2123)
    at _traverse (vue.runtime.esm.js?2b0e:2123)
    at _traverse (vue.runtime.esm.js?2b0e:2123)
    at _traverse (vue.runtime.esm.js?2b0e:2123)
    at _traverse (vue.runtime.esm.js?2b0e:2123)
    at _traverse (vue.runtime.esm.js?2b0e:2123)


Uncaught Error: FIRESTORE (8.3.0) INTERNAL ASSERTION FAILED: Unexpected state
    at x (prebuilt-e065c69d-7a321be7.js?a931:160)
    at t.Sf (prebuilt-e065c69d-7a321be7.js?a931:15880)
    at t.enqueue (prebuilt-e065c69d-7a321be7.js?a931:15795)
    at t.Ps (prebuilt-e065c69d-7a321be7.js?a931:15783)
    at t.As (prebuilt-e065c69d-7a321be7.js?a931:5138)
    at eval (prebuilt-e065c69d-7a321be7.js?a931:5117)
```

何言ってるかわかんないからとても不親切なエラーです。

例えばよくあるのがfirestoreにあるドキュメントで、ある一つのプロパティがDocumentReferenceでデータが渡ってきて、それを持った状態でstoreに保存しちゃうとこんなエラーになっちゃいます。

## 対策

私はTypescriptで組んでいるのですがこんな感じで対策しました。

まずはinterfaceを準備します。

```@types/firestore.d.ts
declare type DocumentReference = import('firebase/app').default.firestore.DocumentReference
declare interface IReferenceWrapper {
  readonly reference: DocumentReference
}
declare type FirestoreDocumentReference = DocumentReference | IReferenceWrapper
```

今回はVillageというエンティティを用意し、ユーザの情報を格納しているとします。

```@types/village.d.ts
declare interface IVillage {
  readonly username: string
  readonly userReference: FirestoreDocumentReference
}
```

そして、今回の肝となるDocumentReferenceをプリミティブで持つラッパーを準備します。

```entity/referenceWrapper.ts
import { firestore } from '~/plugins/firebase' // firestoreは各自いい感じに定義しておいてください

export default class ReferenceWrapper implements IReferenceWrapper {
  private readonly path: string
  get reference(): DocumentReference {
    return firestore.doc(this.path)
  }

  constructor(reference: FirestoreDocumentReference) {
    this.path = (reference as DocumentReference).path
  }
}
```

DocumentReferenceを使ってエンティティとなるクラスを作ります。

```entity/village.ts
import ReferenceWrapper from '~/entity/referenceWrapper'
export class Village implements IVillage {
  readonly username: string
  readonly userReference: ReferenceWrapper

  constructor(obj: IVillage) {
    this.username = obj.username
    this.userReference = new ReferenceWrapper(obj.userReference)
  }
}
```

あとは上記のVillageを初期化してstoreにsetすればちゃんと使えるようになります！

## まとめ

というわけで**vuexのstoreはプリミティブで持つ！**というのを忘れずにというところで、
DocumentReferenceをstoreで持つためのちょっとした工夫を紹介しました。

みなさんのfirebase X nuxtライフが豊かになりますように！

最後に、[ワンナイト人狼オンライン](https://online.1nite-jinro.com/link/blog)というゲームを作ってます！よかったら遊んでね！

https://online.1nite-jinro.com/link/blog

他にも[camecon](https://camecon.me/)や[offcha](https://offcha.com/)といったサービスも作ってるのでよかったら使ってね！

https://camecon.me/

https://offcha.com/