【问题标题】:ReactJS - Using setTimeout() with if/else statementReactJS - 使用 setTimeout() 和 if/else 语句
【发布时间】:2019-07-29 22:32:48
【问题描述】:

我正在研究 React pomodoro 时钟,但我一直坚持使用 setTimeout() 加入 if/else。这是我的代码:

React.useEffect(() => {
    if(props.isOn === true) {
      if(props.timeLeft <= 0) {
        setTimeout(function() {
          if(props.activeClockType === 'Session') {
            props.handleClockType('Break');
            props.handleTimeLeft(props.breakLength * 60);
          } else {
            props.handleClockType('Session');
            props.handleTimeLeft(props.sessionLength * 60);
          }
        }, 1000);
      }
      const id = setInterval(count, 1000);
      return () => clearInterval(id);
    }
  });

当时钟到达 00:00 时,它会停止 1 秒,然后显示 Break 并且计时器设置为 breakLength。太好了,但是 1 秒后它不是倒计时而是变回 Session 并计算 sessionLength。

当我删除 setTimeout (在 CodePen 中评论) 它运行良好,但它不会在 00:00 停止 1 秒,而是会立即改变 Break 看起来很丑的东西。

如何让它在 00:00 停止 1 秒,然后再计算其他 activeClockType?

我整晚都在寻找任何线索,但我仍然不知道出了什么问题。我尝试将 setTimeout 与 if 组合在一起,但效果始终相同。来自this topic,我怀疑我可能应该使用 clearTimeout 但也尝试了任何组合但一无所获。

或者也许我应该尝试另一种方法来运行倒计时而不是 useEffect()?早些时候,我使用 componentWillReceiveProps 在类组件 (在 CodePen 中注释) 中制作了工作计时器,但它已被弃用,我尝试在其他函数上更改它并没有给出任何结果。我不完全理解这个效果挂钩,但这是我 found 计数好的唯一方法。而且它比类组件短。

Here 是我在 CodePen 上的完整代码

提前谢谢你。

【问题讨论】:

    标签: javascript reactjs


    【解决方案1】:

    我认为这里有两个问题:

    1) 每次使用钩子设置状态时,都会触发 React.useEffect,如果设置两次状态,则会触发两次 React.useEffect。

    2) 使用 setTimeout 代替 setInterval 避免更新状态的次数过多。

    问题如下:

      React.useEffect(() => {
        if(props.isOn === true) {
          if(props.timeLeft <= 0 && !changeClockType) {
            setTimeout(function() {
              if(props.activeClockType === 'Session') {
                props.handleClockType('Break');   // ==> Fired update and then excuted React.useEffect
                props.handleTimeLeft(props.breakLength * 60); //  ==> Fired update again and then excuted React.useEffect again
              } else {
                props.handleClockType('Session');
                props.handleTimeLeft(props.sessionLength * 60);
              }
            }, 1000);
          }
          const id = setTimeout(count, 1000);  // ==> modify setInterval to setTimeout 
          return () => clearInterval(id);
        }
    
      });
    

    我尝试这样修改代码:

    let changeClockType = false;  // ==> Should be put here because it couldn't be put in the function component
    function Timer(props) {
    
      const count = () => props.handleTimeLeft(props.timeLeft - 1);
    
      function startStop() {
        props.handleIsOn(props.isOn === false ? true : false);
      }
    
      React.useEffect(() => {
        console.log("step 1");
        if(props.isOn === true) {   console.log("step 2");
          if(props.timeLeft <= 0 && !changeClockType) {   console.log("step 3");
            setTimeout(function() {
              if(props.activeClockType === 'Session') {   console.log("step 4");
                changeClockType = true;
                props.handleClockType('Break');   // ==> Fired update and then excuted React.useEffect
                console.log("Change to Break")
                props.handleTimeLeft(props.breakLength * 60); //  ==> Fired update again and then excuted React.useEffect again
                changeClockType = false;
                console.log("Set breakLength")
              } else {   console.log("step 5");
                changeClockType = true;
                props.handleClockType('Session');
                props.handleTimeLeft(props.sessionLength * 60);
                changeClockType = false;
              }
            }, 1000);
          }
          const id = setTimeout(count, 1000);  // ==> modify setInterval to setTimeout 
          return () => clearInterval(id);
        }
      });
    

    代码不是很干净,但可以很好地运行。

    【讨论】:

    • 完美运行!非常感谢。
    • 顺便说一句,为什么这个 let 在函数外部声明?我试过了,如果在里面声明它实际上是行不通的。如果声明为 useState 钩子,它将起作用。像你那样做或钩子,什么是更好的正常声明?
    • 因为如果将changeClockType放在Timer函数中,当props.handleClockType('Break')执行时,Timer函数会执行, changeClockType 的值将被初始化。并且在 Timer 函数执行后,props.handleTimeLeft(props.breakLength * 60) 会执行。您可以尝试代码并查看登录控制台。