今天來研究一下如何監聽資料的即時變動吧!
先來複習我們之前在查詢資料時,使用 get()
方法的方式吧!當我們呼叫 get()
方法時,會得到一個 Promise 物件,並在之中的 then()
塞入的回呼函式中,會得到一個 DocumentSnapshot 物件作為參數。
let cityRef = db.collection('cities').doc('taichung')
cityRef.get().then(docSnapshot => {
if (docSnapshot.exists) {
let city = docSnapshot.data()
console.log('Document data:', city)
} else {
console.log('No such document!')
}
})
.catch(err => {
console.log('Error getting document', err)
})
而當我們透過 onSnapshot()
方法時,原本傳遞給 then()
的回呼函示則作為該方法的第一個參數,並在第一次拿取資料以及之後每當資料發生變動時再次執行。
db.collection('cities').doc('taichung')
.onSnapshot(docSnapshot => {
console.log('Current data: ', doc.data())
},
error => {
console.log('Error getting document', err)
})
我們可以先來看一下 onSnapshot()
方法的簽署式:
onSnapshot(optionsOrObserverOrOnNext, observerOrOnNextOrOnError, onError) returns function()
他的參數比較複雜,大致分為兩種情況。
當第一個參數原本的放在 thne()
的回呼函式時,onError 回呼函式就會放在第二個位置。這部分就如同上面的程式碼範例。
當第一個參數是選項時,observer object 或是我們原本的放在 thne()
的回呼函式就會放在第二個參數的位置,而 onError 回呼函式就會放在第三個位置。我們會在稍後提到。
當第一個參數是observer object 時,我們原本的放在 thne()
的回呼函式就會放在第二個參數的位置,而 onError 回呼函式就會放在第三個位置。佔部分暫時不舉例。
在最一開始提到 onSnapshot 裡的 OnNext 回呼函式會在第一次拿取資料以及之後每當資料發生變動時再次執行。但需要特別注意的是,這邊的變動也包括本地端寫入後,但還沒寫進 Server 的情況,若要確認這次更新執行的回呼是否已經存到 Server,可以利用 metadata
中的 hasPendingWrites
屬性。
db.collection('cities').doc('taichung')
.onSnapshot(docSnapshot => {
let source = doc.metadata.hasPendingWrites ? 'Local' : 'Server'
console.log(source, 'Current data: ', doc.data())
},
error => {
console.log('Error getting document', err)
})
但是問題來了,onSnapshot()
在預設追蹤的變動是只有資料上的變動,若只是資料的 metadata 變動,是不會觸發的。如果想要追蹤 metadata 的變動,那就要給 onSnapshot()
一個包含 includeMetadataChanges
屬性的選項參數,也就是上述提到的「第一個參數是選項時」的情況。範例如下:
db.collection('cities').doc('taichung')
.onSnapshot({
includeMetadataChanges: true
},
docSnapshot => {
let source = doc.metadata.hasPendingWrites ? 'Local' : 'Server'
console.log(source, 'Current data: ', doc.data())
},
error => {
console.log('Error getting document', err)
})
以 hasPendingWrites
屬性為例:在本地進行資料的變動,雖然資料還尚未寫進去 Server,但這時候仍會因為資料的變動會觸發 OnNext() 回呼函式,且 metadata.hasPendingWrites
為 true
。當資料寫進了 Server,這時候 metadata.hasPendingWrites
變動為 false
,會因為 includeMetadataChanges: true
的關係追蹤到了 metadata 的變動,也觸發了 OnNext() 回呼函式。
當然,如果我們只是想知道資料是否成功寫入 Server 但沒有要追蹤後續,比起使用 onSnapshot()
搭配 includeMetadataChanges: true
,更適合使用之前研究到的 set()
、 get()
、 update()
、 delete()
,因為我們是將回呼函式傳遞到他們回傳的 Promise 物件的 .then()
方法中,所以一定是資料寫入 Server 後才會觸發。
今天就研究到這邊,明天再來研究如何一次追蹤一個集合中多個文件的變動、針對不同的變動做出不同的對應、以及如何取消追蹤吧!