Bug on a timer made with requestAnimationFrame

60 Views Asked by At

I made a timer for a card game and everything was working fine to this point. But I wanted to stop the timer when there's a winner and stop the timer animation by the way. So basically with the following code the when there's a winner the timer will only stop at the end of the current turn before going back to idle state (when value is null).

useEffect(() => {

    if (turnTimerStartedAt && !winnerPlayerId) {
      const startedAt =
        Date.now() - (Rune.gameTimeInSeconds() - turnTimerStartedAt) * 1000;

      const tick = () => {
        const newValue = MAX_TURN_TIME - (Date.now() - startedAt) / 1000 - 0.5;
        if (newValue <= 0) {
          setValue(0);
        } else {
          setValue(newValue);
          requestAnimationFrame(tick);
        }
      };
      tick();
    } else {
      setValue(null);
    }

}, [turnTimerStartedAt, winnerPlayerId]);

I want it to stop it as soon as a winnerPlayerId is set and noticed it doesn't stop when any of the variables (turnTimerStartedAt or winnerPlayerId) make the condition falsy.

Is there a way to make the timer stop when the dependencies change ?

1

There are 1 best solutions below

2
Ori Drori On BEST ANSWER

You need to cancel the requestAnimationFrame by assigning the requestId to a variable, and cancelling the request using cancelAnimationFrame.

In this case, I would use the cleanup function of useEffect to cancel the animation frame, whenever the dependencies change:

useEffect(() => {
  let requestId;
  
  if (turnTimerStartedAt && !winnerPlayerId) {
    const startedAt =
      Date.now() - (Rune.gameTimeInSeconds() - turnTimerStartedAt) * 1000;
    const tick = () => {
      const newValue = MAX_TURN_TIME - (Date.now() - startedAt) / 1000 - 0.5;
      if (newValue <= 0) {
        setValue(0);
      } else {
        setValue(newValue);
        requestId = requestAnimationFrame(tick); // set the requestId
      }
    };
    tick();
  } else {
    setValue(null);
  }
  
  return () => {
    cancelAnimationFrame(requestId); // cancel the animation frame
  };
}, [turnTimerStartedAt, winnerPlayerId]);