くらげになりたい。

くらげのようにふわふわ生きたい日曜プログラマなブログ。趣味の備忘録です。

FirestoreのonSnapshotでリアルタイム同期する

firestore便利だけど、getしか使ったことなかった。。 onSnapshotを使うと、変更されたデータを受け取れるので、試してみたときの備忘録。

ドキュメントはこれをみた。
Cloud Firestore でリアルタイム アップデートを入手する  |  Firebase

使い方

こんな感じ。

// 対象のquery
const query = db.collection("...").orderBy("createAt", "desc");

// リッスンの開始
this.unset = query.onSnapshot(snapshot => {
  snapshot.docChanges().forEach((change: DocumentChange<DocumentData>) => {
    // 変更のタイプ
    console.info(`*** change: type=${change.type}`);
    // 対象のドキュメント。変更の場合は、変更後
    console.info(`*** change: data=${JSON.stringify(change.doc.data(), null, 2)}`);
    // refに対する変更後のindex。削除時は-1
    console.info(`*** change: newIndex=${change.newIndex}`);
    // refに対する変更前のindex。追加時は-1
    console.info(`*** change: oldIndex=${change.oldIndex}`);

    if (change.type === "added") {
      // 追加時
    } else if (change.type === "modified") {
      // 変更時
    } else if (change.type === "removed") {
      // 削除時
    }
  });
});

// リッスンをやめる。返ってきた関数を呼び出すとデタッチする
this.unset()

この例は、collectionだけど、collectionGroupdocでも同じ感じのよう。

ハマった点: リッスン開始すると対象すべてがadded

snapshot.docChanges()を呼び出すと、
・変更を受け取る対象が全ドキュメントになり、
・さらに、最初はaddedされる

ドキュメントには、

f:id:wannabe-jellyfish:20200524174314p:plain

と書かれるけど、件数が多いとちょっと困る。。

「初期表示は10件で、そのあとは変更を受け取りたい」。。

なので、whereやendBeforeなどを使って、範囲を絞り込む必要がある。

// 初期表示分の取得
const initQuery = db.collection("...").orderBy("createAt", "desc").limit(10);
const initdata = await initQuery.get();

// 最後のデータを取得
const lastData = this.getLastData(initdata); // 中身は略

// 最後のデータ以降をリッスン
const query = db.collection("...").orderBy("createAt", "desc").endBefore(lastData.createAt);
this.unset = query.onSnapshot(snapshot => {
  snapshot.docChanges().forEach((change: DocumentChange<DocumentData>) => {
    // 略
  });
});

以上!!

【PR】これをつかって、こんなのつくりました!

エアで投げ銭できるWebサービス「エア銭」

air-money.netlify.app

よかったら、遊んでみてください(´ω`)

参考にしたサイトさま