React,无法访问传递给 useEffect() 中 setInterval() 的函数内部状态变量的更新值

Rya*_*fer 5 javascript setinterval reactjs use-effect use-state

我正在使用 React 构建一个简单的时钟应用程序。目前该countDown()功能有效,但我希望用户能够通过按下按钮来停止/启动时钟。我有一个名为的状态布尔值,paused当用户单击按钮时该状态布尔值会反转。问题在于,在 的值反转后,对传递给的函数内部paused的引用似乎正在访问 的默认值,而不是更新后的值。pausedcountDown()setInterval()paused

function Clock(){
  const [sec, setSecs] = useState(sessionLength * 60);
  const [paused, setPaused] = useState(false);
 
  const playPause = () => {
    setPaused(paused => !paused);
  };
  
  const countDown = () => {
    if(!paused){
        setSecs(sec => sec - 1)
      }
  }
  
  useEffect(() => {
    const interval = setInterval(() => {
        countDown();
    }, 1000);
    return () => {
      clearInterval(interval);
    };
  }, []);
Run Code Online (Sandbox Code Playgroud)

setState()我假设它与 React 中调用的异步性质和/或使用正则表达式时范围/上下文的性质有关。然而,我无法通过阅读与这些概念相关的文档来确定发生了什么。

我可以想到一些解决方法,让我的应用程序能够按需要运行。但是我想了解我当前的方法有什么问题。我将不胜感激任何人都可以阐明这一点!

Kev*_*off 4

在您的代码中,useEffect仅在安装组件时调用一次。

里面注册的倒计时函数在调用时会有初始值useEffect/setInterval。所以paused只有在您最初安装组件时才会有该值。因为您没有直接调用 countDown 或更新其内部的值useEffect,所以它不会更新。

我认为你可以这样解决这个问题:

  interval.current = useRef(null);
  const countDown = () => {
    if(!paused){
        setSecs(sec => sec - 1)
      }
  }
  
  useEffect(() => {
    clearInterval(interval.current);
    interval.current = setInterval(countDown, 1000);
    return () => {
      clearInterval(interval.current);
    };
  }, [paused]);
Run Code Online (Sandbox Code Playgroud)

您的useEffect值取决于 的值,paused因为它需要创建一个新的间隔(使用不同的倒计时函数)。这将触发useEffect不仅会在安装时触发,而且每次paused更改时都会触发。因此,一种解决方案是清除间隔并使用不同的回调函数开始新的间隔。

编辑:您实际上可以改进它,因为您只希望在 countDown 函数实际执行某些操作时运行间隔,因此这也应该有效:

 useEffect(() => {
    clearInterval(interval.current);
    if(!paused) {
      interval.current = setInterval(countDown, 1000);
    }
    return () => {
      clearInterval(interval.current);
    };
  }, [paused]);
Run Code Online (Sandbox Code Playgroud)