#javascript #reactjs #react-native
#javascript #reactjs #реагировать — родной
Вопрос:
Введение
У меня на экране есть индикатор времени выполнения, который расширяется в useEffect, внутри setInterval. Проблема возникает, когда запускается мой прослушиватель БД и выполняет его обратный вызов, где код обновляет другие состояния.
Код
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 amp;amp; timerProgress < 1) {
// Advance the timer every second
var timerInterval = setInterval(() => {
advanceTimer();
}, 1000);
}
return () => {
clearInterval(timerInterval);
};
}, [timerProgress]);
Проблема
Проблема в том, что значение timerProgress сбрасывается каждый раз, когда выполняется обратный вызов слушателя.
Например, если я выполняю console.log(timerProgress) внутри обратного вызова прослушивателя
const listenRoomChanges = (roomDoc) => {
const { full } = roomDoc.data();
console.log(timerProgress); // <----
setIsRoomFull(full);
};
он покажет мне 0, например, когда реальное значение равно 10 секундам.
Мой текущий обходной путь
Если при обновлении хода выполнения таймера я делаю
const advanceTimer = () => {
setTimerProgress(
(prevTimerProgress) => prevTimerProgress 1 / GAME_DURATION
);
};
вместо этого timerProgress не будет «сбрасываться» на 0, когда обратный вызов прослушивателя БД обновляет другие состояния…
Но по какой-то причине это работает не на 100% хорошо (когда обратный вызов прослушивателя БД обновляет другие состояния, timerProgress ускоряет nStateUpdatesInTheDbListenerCallback в разы быстрее, я не знаю причину, но это происходит).
Кто-нибудь знает, как решить эту проблему? Какой-либо специальный хук для этого? Почему это происходит?
Я думаю, что я могу использовать useRef(), но это не приведет к повторному рендерингу…
Спасибо.
Ответ №1:
Удалить return () => { clearInterval(time interval);};
От
useEffect(() => {
if (timerProgress > 0 amp;amp; timerProgress < 1) {
// Advance the timer every second
var timerInterval = setInterval(() => {
advanceTimer();
}, 1000);
}
return () => {
clearInterval(timerInterval);
};
}, [timerProgress]);
И дайте мне знать,
Комментарии:
1. Не сработало. timeInterval воссоздается снова и снова при изменении timerProgress. Вот почему я его очищаю. Если я это сделаю, временной интервал будет глючить, увеличиваясь и уменьшаясь.
2. Хорошо, просто удалите timerProgress из
useEffect(() => { if (timerProgress > 0 amp;amp; timerProgress < 1) { // Advance the timer every second var timerInterval = setInterval(() => { advanceTimer(); }, 1000); } return () => { clearInterval(timerInterval); }; }, []);
3. Вы хотите предоставить пустой массив в качестве второго аргумента useEffect, чтобы функция запускалась только один раз после первоначального рендеринга. Из-за того, как работают замыкания, это заставит переменную counter всегда ссылаться на начальное значение. Вместо этого вы можете использовать функциональную версию setTimerProgress, чтобы всегда получать правильное значение.
4.
useEffect(() => { const interval = setInterval(() => { setTimerProgress((prevTimerProgress) => prevTimerProgress 1 / GAME_DURATION) }, 1000); return () => { clearInterval(interval); }; }, []);