S
S
somefhjs2021-09-05 13:44:27
React
somefhjs, 2021-09-05 13:44:27

Why is the connection to the fresh state lost when using requestAnimationFrame?

I have already come up with a crutch on how to fix this, but I really want to gain a real understanding of why this is happening. Here is a simplified piece of code:

export default function SomeComponent() {
  const [playing, setPlaying] = useState(false);

  const togglePause = () => {
    const newPlaying = !playing;
    setPlaying(newPlaying);

    if (newPlaying) {
      start();
    } else {
      stop();
    }
  };

  const start = () => {
    log();
  };

  const stop = () => {
    // some code
  };

  const log = () => {
    // console.log('log(): ', playing);

    if (playing) requestAnimationFrame(log);
  };

  return (
    <div>
      <div className={styles.btn}>
        <AppButton onClick={togglePause}>
          { playing ? 'Stop' : 'Start' }
        </AppButton>
      </div>
    </div>
  );
}


Look at the log() function. In it, I'm trying to stop the requestAnimationFrame(log) recursion in case the variable playingbecomes false. Unfortunately, during the recursion, the log() function for some reason stops receiving a fresh state and never knows that the variable playinghas become false. At the same time, the value of the variable changes correctly and this can be seen both in the rendered template and in other functions. I tried moving the control function call after changing the value playingto the hook useEffect():

...
  const togglePause = () => {
    setPlaying(!playing);
  };

  useEffect(() => {
    if (playing) {
      startListening();
    } else {
      stopListening();
    }
  }, [playing])
  ...


Everything worked correctly, but the problem with the function log()did not go away. As a result, I started another variable - playingRefusing useRef(). It completely duplicated the variable playingand changed simultaneously with it. This allowed me to logrely on in the function playingRefbecause it was always fresh, and in JSX to rely on playingbecause it playingRefdid not respond to value changes. In total, it turns out that everything works, but most importantly, I did not understand why the log()fresh state was not picked up inside the recursion.

Answer the question

In order to leave comments, you need to log in

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question