我正在构建Pomodoro计时器,计数器应从25分钟开始递减直到0,然后再运行5分钟递减计时器。到目前为止,计时器和中断状态应被重置以准备再次运行。

但是,我在按一下按钮运行时重置时间时遇到问题。我有一个resetTimer函数,当前正在使用它在每个周期结束时重新初始化计时器和中断计数,但是如果在计时器或中断运行时(计数到0)单击,那么resetTimer确实确实运行了一秒钟,但是然后计时器会像按钮触发事件之前一样继续操作。

我已经尝试过调用resetTimerreturn,如果条件resetClicked === true调用了该函数并短暂运行但随后使用原始计时器继续运行。

我也尝试过设置一个初始化函数,该函数在调用resetClicked === true函数之前先检查start()是否为resetTimer,然后再在ojit_code中再次调用此函数,但它也不起作用。

代码

import React, { useState } from "react";

export default function App() {
  const [sessionLength, setSessionLength] = useState(25);
  const [breakLength, setBreakLength] = useState(5);
  const [timerRunning, setTimerState] = useState(false);
  const [breakRunning, setBreakState] = useState(false);

  let countdown;
  let minutesToSecondsSession = sessionLength * 60;
  let minutestoSecondsBreak = breakLength * 60;
  const clear = () => clearInterval(countdown);

  const start = () => {
    if (timerRunning === false) {
      setTimerState(true);
      console.log("timer started");
      const now = Date.now();
      const then = now + minutesToSecondsSession * 1000;

      displayTimeLeftSession(minutesToSecondsSession);

      countdown = setInterval(() => {
        const secondsLeft = Math.round((then - Date.now()) / 1000);

        if (secondsLeft === 0) {
          clear(countdown);
          console.log("timer interval cleared");
          breakTimer();
        }

        displayTimeLeftSession(secondsLeft);
      }, 1000);
    }
  };
  // end of timer function
  //
  // start of break timer
  function breakTimer() {
    if (breakRunning === false) {
      setBreakState(true);
      console.log("break timer started");
      const now = Date.now();
      const then = now + minutestoSecondsBreak * 1000;

      displayTimeLeftBreak(minutestoSecondsBreak);

      countdown = setInterval(() => {
        const secondsLeft = Math.round((then - Date.now()) / 1000);

        if (secondsLeft === 0) {
          console.log("break interval cleared");
          resetTimer();
          return;
        }

        displayTimeLeftBreak(secondsLeft);
      }, 1000);
    }
  }

  function displayTimeLeftSession(minutesToSecondsSession) {
    const minutes = Math.floor(minutesToSecondsSession / 60);
    const remainderSeconds = minutesToSecondsSession % 60;

    setSessionLength(`${minutes}:${remainderSeconds}`);
  }

  function displayTimeLeftBreak(minutesToSecondsBreak) {
    const minutes = Math.floor(minutesToSecondsBreak / 60);
    const remainderSeconds = minutesToSecondsBreak % 60;

    setBreakLength(`${minutes}:${remainderSeconds}`);
  }

  // end of display timer logic

  function incrementSession() {
    if (sessionLength <= 60) {
      setSessionLength(prev => prev + 1);
    }
  }

  function decrementSession() {
    if (sessionLength > 1) {
      setSessionLength(prev => prev - 1);
    }
  }

  function incrementBreak() {
    if (breakLength < 60) {
      setBreakLength(prev => prev + 1);
    }
  }

  function decrementBreak() {
    if (breakLength > 1) {
      setBreakLength(prev => prev - 1);
    }
  }

  const resetTimer = () => {
    clear(countdown);
    setTimerState(false);
    setBreakState(false);
    setSessionLength(0.05);
    setBreakLength(0.05);
    console.log("reset");
  };

代码一直到return()为止,省略了JSX以节省空间,反正​​只是将计时器呈现到页面上,添加嵌入时遇到问题。

https://codesandbox.io/s/fcc-pomodoro-clock-3rxfh?fontsize=14&hidenavigation=1&theme=dark

编辑:
尝试将每个倒计时定义为单独的全局范围变量,然后在需要时为每个倒数调用clearInterval,但仍然存在相同的问题。
import React, { useState, useRef } from "react";

export default function App() {
  const [breakLength, setBreakLength] = useState(0.05);
  const [sessionLength, setSessionLength] = useState(20);
  const [timerRunning, setTimerState] = useState(false);
  const [breakRunning, setBreakState] = useState(false);

  let sessionCountdown;
  let breakCountdown;
  let minutesToSecondsSession = sessionLength * 60;
  let minutestoSecondsBreak = breakLength * 60;
  //const clear = () => clearInterval(countdown);

  const start = () => {
    if (timerRunning === false) {
      setTimerState(true);
      console.log("timer started");
      const now = Date.now();
      const then = now + minutesToSecondsSession * 1000;

      displayTimeLeftSession(minutesToSecondsSession);

      sessionCountdown = setInterval(() => {
        const secondsLeft = Math.round((then - Date.now()) / 1000);

        if (secondsLeft === 0) {
          clearInterval(sessionCountdown);
          console.log("timer interval cleared");
          breakTimer();
        }

        displayTimeLeftSession(secondsLeft);
      }, 1000);
    }
  };
  // end of timer function
  //
  // start of break timer
  function breakTimer() {
    if (breakRunning === false) {
      setBreakState(true);
      console.log("break timer started");
      const now = Date.now();
      const then = now + minutestoSecondsBreak * 1000;

      displayTimeLeftBreak(minutestoSecondsBreak);

      breakCountdown = setInterval(() => {
        const secondsLeft = Math.round((then - Date.now()) / 1000);

        if (secondsLeft === 0) {
          console.log("break interval cleared");
          clearInterval(breakCountdown);
          resetTimer();
          return;
        }

        displayTimeLeftBreak(secondsLeft);
      }, 1000);
    }
  }

  function displayTimeLeftSession(minutesToSecondsSession) {
    const minutes = Math.floor(minutesToSecondsSession / 60);
    const remainderSeconds = minutesToSecondsSession % 60;

    setSessionLength(`${minutes}:${remainderSeconds}`);
  }

  function displayTimeLeftBreak(minutesToSecondsBreak) {
    const minutes = Math.floor(minutesToSecondsBreak / 60);
    const remainderSeconds = minutesToSecondsBreak % 60;

    setBreakLength(`${minutes}:${remainderSeconds}`);
  }

  // end of display timer logic

  function incrementSession() {
    if (sessionLength <= 60) {
      setSessionLength(prev => prev + 1);
    }
  }

  function decrementSession() {
    if (sessionLength > 1) {
      setSessionLength(prev => prev - 1);
    }
  }

  function incrementBreak() {
    if (breakLength < 60) {
      setBreakLength(prev => prev + 1);
    }
  }

  function decrementBreak() {
    if (breakLength > 1) {
      setBreakLength(prev => prev - 1);
    }
  }

  const resetTimer = () => {
    clearInterval(sessionCountdown);
    clearInterval(breakCountdown);
    setTimerState(false);
    setBreakState(false);
    setSessionLength(0.05);
    setBreakLength(0.05);
    console.log("reset");
  };

最佳答案

TL; DR; 使用可以看到正在工作的沙箱here

详细信息:

每次执行render时,都会创建resetTimer()和其他函数的新闭包。如果将console.log(countdown)放在resetTimer()声明之前,您会看到countdown总是 undefined就是

  • 我真的怀疑在每个渲染器上创建新函数是一个好主意。
  • 但是要解决此特定问题,您可以将setInterval的结果保存为状态,以便稍后在resetTimer()中使用

    const [countdown, setCountdown] = useState(undefined);
    

    但是在setInterval内部,您仍然可以使用闭包,这更容易

    const interval = setInterval(() => {
      ...
      if (secondsLeft === 0) {
        clearInterval(interval);
        ...
      }
      ...
    }, 1000);
    setCountdown(interval) // save it to the state
    
  • 关于javascript - 单击按钮时未重置为间隔运行的计数器,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/60738689/

    10-12 13:22
    查看更多