I have a form and on submit I add a doc with the data to my firestore database. I have a realtime listener on the collection I add the doc to.
onSubmit={async(e) => {
e.preventDefault()
const form = document.querySelector(".activityForm")
const colRef = collection(db, "inactivity")
await addDoc(colRef, {
name: user.displayName,
from: form.from.value,
to: form.to.value,
reason: form.reason.value.trim(),
reported: serverTimestamp()
})
.then(() => {
console.log("Success!")
form.reset()
})
.catch((err) => {
console.log(err)
})
const q = query(colRef, orderBy("reported", "desc") );
const unsubscribe = onSnapshot(q, (querySnapshot) => {
setInactiveReps([])
querySnapshot.forEach((doc) => {
setInactiveReps(prev => {
return [...prev, doc.data()]
})
})
});
}
After, I am trying to map through and display all the docs in the collection.
{inactiveReps && inactiveReps.map(report => (
date = formatDistance(
new Date(report.reported.seconds*1000),
new Date(),
{
addSuffix: true,
includeSeconds: true
}
),
console.log(report),
<tr key={report.id} className="h-auto">
<td className="text-white pl-[21px]">{report.name}</td>
<td className="text-white pl-[21px]">{report.from}</td>
<td className="text-white pl-[21px]">{report.to}</td>
<td className="text-white pl-[21px]">{report.reason}</td>
<td className="text-white pl-[21px]">{date}</td>
</tr>
))
}
It worked fine.. until I added the realtime listener. Now I get the following error:
TypeError: can't access property "seconds", report.reported is null
, when its clearly not null, if I log the doc its shown there:
...
reported: Object { seconds: 1664099305, nanoseconds: 349000000 }
...
What could cause this?
CodePudding user response:
I haven't tested your code but the problem most probably comes from the fact that serverTimestamp()
"includes a server-generated timestamp in the written data".
So at the moment you execute the addDoc()
method in your front end, the value of the reported
field is not set in your front-end because it is the server/back-end that sets it. But, due to the "latency compensation", your listeners will be notified with the new data before the data is sent to the backend, resulting to the fact that report.reported
is null
.
You therefore need to indicate to the listener set with onSnapshot()
that it has to only treat events coming from the server/back-end. This can be done with metadata.hasPendingWrites
as follows (untested):
const unsubscribe = onSnapshot(q, (querySnapshot) => {
setInactiveReps([])
querySnapshot.forEach((doc) => {
if (doc.metadata.hasPendingWrites) {
// Do whatever you want: skip the doc or add a "pending" value for the timestamp
} else {
setInactiveReps(prev => {
return [...prev, doc.data()]
})
}
})
});