I'm building a simple firebase app. The main textarea is linked to an entry on Firebase. When the user types, I debounce
the keyup event (300ms) and I do an update on Firebase.
Every .update
event triggers a onSnapshot
event when it comes back from the server (which is the expected behaviour of Firebase). My problem is that it disturbs the user while they type. Because there is a delay between the time the request goes to the server and when it comes back. When it comes back the user has already typed more text and the content of the textarea is different that when the .update
was fired.
Here's my code and a jsFiddle that illustrates the problem:
var textareaElement = document.querySelector('textarea');
// Initialize Firebase
var config = {
apiKey: "XXX",
authDomain: "XXX",
projectId: "XXX",
};
firebase.initializeApp(config);
const db = firebase.firestore();
const doc = db.collection("notes").doc('XXX');
// This is debounced
const update = function(){
doc.update({
text: textareaElement.value,
updated_at: firebase.firestore.FieldValue.serverTimestamp()
})
}
// Update the server when we change the client
textareaElement.addEventListener('keyup', _.debounce(update, 300));
// Listen for changes from the server
doc.onSnapshot(function(doc){
textareaElement.value = doc.data().text
});
jsFiddle
https://jsfiddle.net/x6h80keb/2 (type at a normal pace and you will see what I mean)
jsFiddle - without serverTimestamp()
https://jsfiddle.net/x6h80keb/6/
I found that removing the call to the serverTimestamp()
function helps a lot. It looks like it's responsible for the delay of the request. It makes the "bug" almost unnoticeable, but it still happens sometimes, when the request takes a bit longer to execute.
The only solution I can think of would be to detect the initiator of the update. Whenever a onSnapshot
event is triggered, we could look into the doc
object, and tell if the change has been made from this client or from another one. But there isn't such a feature apparently.
Any ideas how I could overcome this problem ?
EDIT:
I ended up increasing the debounce delay. Instead of updating after 300ms, I wait a few seconds. This way the user typing can not be disturbed. I'll also freeze the textarea during the .update.
This is because the contents of the written document may be different than what the user has typed in since the document was updated.
Rethink this code:
This is going to overwrite whatever the user has typed since the debounce triggered and the document was updated with their progress. It will be especially painful on networks with high latency.
Consider instead not rewriting the entire textarea on each document change, and instead showing an indicator for the sync status of their typing. Is their typing in sync or not? That's all the user needs to know. You don't need to overwrite their typing on a regular basis.