LoginSignup
70
55

More than 3 years have passed since last update.

firestoreのonSnapshotを使う際に気を付けたいこと

Posted at

firestoreのコレクションに対するonSnapshotの覚書です。

対象

firestoreにあるコレクションの追加・削除・変更をクライアントサイドにリアルタイムに反映したい場合、次のようにonSnapshotを使って変更をリッスンすることができます。

client.js
const db = firebase.firestore();

db.collection('todos')
    .onSnapshot(function (querySnapshot) {
        for (let change of querySnapshot.docChanges()) {
            if (change.type === 'added') {
                // データが追加された時
            }
            else if (change.type === 'modified') {
                // データが変更された時
            }
            else if (change.type === 'removed') {
                // データが削除された時
            }
        }
    })

注意したいケース

最初の呼び出し

公式にも記載されていますが、onSnapshotを実行すると最初に全てのドキュメントを取得します。この時、change.typeaddedでコールされます。これは仮にfirestoreのコレクションが空の場合でも、関数自体の呼び出しは発生します。

client.js

db.collection('todos')
    .where('user', '==', user)
    .onSnapshot(function (querySnapshot) {
        for (let change of querySnapshot.docChanges()) {
            if (change.type === 'added') {
                // データが追加された時
                // もしくは最初の呼び出し
            }
            else if (change.type === 'modified') {
                // データが変更された時
            }
            else if (change.type === 'removed') {
                // データが削除された時
            }
        }
    })

limitクエリとの併用

onSnapshotはコレクションに対するクエリと併用ができます。
特に、limitorderByのようなクエリを併用する場合に注意が必要です。
例えば、以下のクエリを実行するとします。

client.js
// 作成日付が新しいものから5タスク購読する
db.collection('todos')
    .where('user', '==', user)
    .orderBy('createDate','desc')
    .limit(5)
    .onSnapshot(function (querySnapshot) {
        
    })

firestoreには以下のデータが入っていると想定します。

todos.json
{
 {"createDate":"2020/1/20", "title":"テスト1"},
 {"createDate":"2020/1/19", "title":"テスト2"},
 {"createDate":"2020/1/18", "title":"テスト3"},
 {"createDate":"2020/1/17", "title":"テスト4"},
 {"createDate":"2020/1/16", "title":"テスト5"},
 {"createDate":"2020/1/15", "title":"テスト6"},
}

onSnapshot実行直後に、テスト1〜テスト5の5つのTODOが取得できます。これは想定通りです。

client.js

    .onSnapshot(function (querySnapshot) {
        for (let change of querySnapshot.docChanges()) {
            if (change.type === 'added') {
                //テスト1〜テスト5が入ってくる
            }
            
        }
    })

次に新しいTODOをfirestoreに投入します。

todos.json
{
 {"createDate":"2020/1/21", "title":"テスト0"},
}

すると、onSnapshotではlimit(5)によって購読するデータの入れ替えが発生し、change.typeaddedremovedが呼び出されます。

client.js

db.collection('todos')
    .where('user', '==', user)
    .orderBy('createDate','desc')
    .limit(5)
    .onSnapshot(function (querySnapshot) {
        for (let change of querySnapshot.docChanges()) {
            if (change.type === 'added') {
                // テスト0が入ってくる
            }
            else if (change.type === 'modified') {

            }
            else if (change.type === 'removed') {
                // テスト5が入ってくる
            }
        }
    })

考えてみれば、テスト0の追加によって、5つの購読対象の中からテスト5が押し出されるというのは自然なことなのですが、limitを使用しない時の感覚で使ってしまうと、removedによってあたかもstoreから削除されたと判断しかねないため、注意が必要です。

同様に、次のデータの中からテスト3を削除してみます。

todos.json
{
 {"createDate":"2020/1/20", "title":"テスト1"},
 {"createDate":"2020/1/19", "title":"テスト2"},
 {"createDate":"2020/1/18", "title":"テスト3"},
 {"createDate":"2020/1/17", "title":"テスト4"},
 {"createDate":"2020/1/16", "title":"テスト5"},
 {"createDate":"2020/1/15", "title":"テスト6"},
}

こちらの結果はもう予想がつくかもしれませんが、テスト3が5つの購読リストから除外されたために、テスト6が代わりに購読対象に加わります。

client.js

db.collection('todos')
    .where('user', '==', user)
    .orderBy('createDate','desc')
    .limit(5)
    .onSnapshot(function (querySnapshot) {
        for (let change of querySnapshot.docChanges()) {
            if (change.type === 'added') {
                // テスト6が入ってくる
            }
            else if (change.type === 'modified') {

            }
            else if (change.type === 'removed') {
                // テスト3が入ってくる
            }
        }
    })

所感

limitクエリは、Infinite Scrollのように少しずつリッスンしたいときなどに考慮が必要かなと。

70
55
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
70
55