Your problem comes from the "latency compensation": "Local writes in your app will invoke snapshot listeners immediately. ... When you perform a write, your listeners will be notified with the new data before the data is sent to the backend". See the doc on onSnapshot()
.
Since you use firebase.firestore.FieldValue.serverTimestamp()
to set the value of createdAt
(which is a good approach), the value of createdAt
is calculated by the backend (the serverTimestamp
sentinel is replaced by a server-generated timestamp in the written data).
Therefore, at the moment the snapshot listener is invoked in your front-end following the local write ("Local writes in your app will invoke snapshot listeners immediately"), this value is not set (item.createdAt.toDate()
generates an error).
One solution is to use the metadata.hasPendingWrites
property that indicates whether the document has local changes that haven't been written to the backend yet.
For example:
const ChatMessages = useCallback(() => {
privateMessage
.doc(chatId)
.collection('messages')
.orderBy('createdAt', 'asc')
.onSnapshot((querySnapshot) => {
const messageList = [];
querySnapshot.forEach((doc) => {
messageList.push(doc.data());
})
if (!querySnapshot.metadata.hasPendingWrites) { // <======
setMessages(messageList);
}
}, error => {
console.log(error)
})
}, [chatId])
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…