javascript 为什么即使在组件渲染之后,prior useEffect()仍在运行相当长的时间?

nbysray5  于 2023-02-11  发布在  Java
关注(0)|答案(2)|浏览(247)

我在ReactJs中实现秒表,这是我的代码现在的样子:

const App: React.FC = () => {
  const [seconds, setSeconds] = useState(0);
  const [isPaused, setIsPaused] = useState(false);

  const secondsToTimerFormat = (seconds: number): string => {
    console.log(seconds)
    return (seconds-seconds%60)/60+":"+seconds%60
  }

  const manipulateTimer = (toPauseTimer: boolean) => {
    setIsPaused(toPauseTimer);
  }

  useEffect(() => {
    if(!isPaused){
      setTimeout(() => {
        setSeconds(seconds + 1)
      }, 1000)
    }
  }, [seconds, isPaused])

  return (
    <div className="App">
      {secondsToTimerFormat(seconds)}
      <div>
        <button onClick={() => {manipulateTimer(true)}}>Pause</button>
        <button onClick={() => {manipulateTimer(false)}}>Resume</button>
        <button onClick={() => {
          setSeconds(0);
        }}>Reset</button>
      </div>
    </div>
  );
}

我希望它能正常工作。但是“Reset”按钮没有正常工作。如果我在13秒后点击“Reset”,这就是console.log()的输出。

如果我在useEffect()中添加一个新变量,例如let execute: boolean = true,然后在useEffect()清理中将其设置为false,则一切都按预期工作。
所以,我知道修复,但我想知道当前行为背后的原因。我知道当我点击重置时,已经有一个useEffect()运行,秒值为13。但由于它的setTimeout()在一秒内结束,同时,我正在执行setSeconds(0),为什么以前的useEffect()在停止前会运行多次?

v6ylcynt

v6ylcynt1#

尝试使用setInterval和单独的方法来处理计时器状态:

import { useState } from "react";

export default function App() {
  const [seconds, setSeconds] = useState(0);
  const [intervalId, setIntervalId] = useState(0);

  const secondsToTimerFormat = (seconds) => {
    console.log(seconds);
    return (seconds - (seconds % 60)) / 60 + ":" + (seconds % 60);
  };

  const handleStart = () => {
    const id = setInterval(() => {
      setSeconds((prev) => prev + 1);
    }, 1000);
    setIntervalId(id);
  };

  const handlePause = () => {
    clearInterval(intervalId);
  };

  const handleReset = () => {
    handlePause();
    setSeconds(0);
  };

  return (
    <div className="App">
      {secondsToTimerFormat(seconds)}
      <div>
        <button
          onClick={() => {
            handlePause();
          }}
        >
          Pause
        </button>
        <button
          onClick={() => {
            handleStart();
          }}
        >
          Resume
        </button>
        <button
          onClick={() => {
            handleReset();
          }}
        >
          Reset
        </button>
      </div>
    </div>
  );
}

Link到沙箱

fae0ux8s

fae0ux8s2#

像这样的问题通常是因为正在使用的计时器在渲染之间没有被清除。另外,当下一个状态依赖于当前状态时,最好使用第二种形式的状态设置器函数,它将当前状态作为参数并返回下一个状态。修改useEffect如下所示,以使其工作:

useEffect(() => {
    let timer;
    if (!isPaused) {
      timer = setTimeout(() => {
        setSeconds((seconds) => seconds + 1);
      }, 1000);
    }

    return () => {
      if (timer) clearTimeout(timer);
    };
}, [seconds, isPaused]);

相关问题