Introduction
I have a progress time bar on my screen which is advanced in a useEffect, inside a setInterval. The problem comes when my DB listener is triggered and executes its callback, where the code updates other states.
Code
const [isRoomFull, setIsRoomFull] = useState(false);
const [timerProgress, setTimerProgress] = useState(0);
...
const startGame = () => {
...
advanceTimer();
}
const listenRoomChanges = (roomDoc) => {
const { full } = roomDoc.data();
setIsRoomFull(full);
};
const advanceTimer = () => {
setTimerProgress(timerProgress + 1 / GAME_DURATION);
};
useEffect(() => {
if (roomId) {
const { firebase } = props;
roomListener.current = firebase
.getDatabase()
.collection("rooms")
.doc(roomId)
.onSnapshot(listenRoomChanges); // <- DB listener callback
}
return () => {
detachListener();
};
}, [roomId]);
useEffect(() => {
if (timerProgress > 0 && timerProgress < 1) {
// Advance the timer every second
var timerInterval = setInterval(() => {
advanceTimer();
}, 1000);
}
return () => {
clearInterval(timerInterval);
};
}, [timerProgress]);
Problem
The problem is that the timerProgress value is reset every time the listener callback is executed.
For example, if I do a console.log(timerProgress) inside the listener callback
const listenRoomChanges = (roomDoc) => {
const { full } = roomDoc.data();
console.log(timerProgress); // <----
setIsRoomFull(full);
};
it will show me 0, when the real value is 10 secs, for example.
My current workaround
If when updating the timer progress I do
const advanceTimer = () => {
setTimerProgress(
(prevTimerProgress) => prevTimerProgress + 1 / GAME_DURATION
);
};
instead, the timerProgress will not be "reset" to 0 when the DB listener callback updates other states...
But for some reason, this is not working 100% well (when the DB listener callback updates other states, the timerProgress advances nStateUpdatesInTheDbListenerCallback times faster, I don't know the reason but this is happening).
Does someone know how to solve this problem? Any special hook for this? Why is this happening?
I think that I can use useRef() but this will not fire a re-render...
Thank you.