【问题标题】:a count-down component with useEffect not working as expected具有 useEffect 的倒计时组件未按预期工作
【发布时间】:2021-07-02 06:50:54
【问题描述】:

我正在学习对钩子做出反应。我写了一个倒计时组件。 useEffect 让我很困惑。 当计时器转到00:00时,它不能显示00:00,只需转到00:10。有人可以帮忙吗? 下面是一个演示。

count-down

export default function App() {
  const [timer, setTimer] = useState(dayjs().minute(0).second(10));
  const [status, setStatus] = useState(true);

  const handleReset = () => {
    setStatus(true);
    setTimer(dayjs().minute(0).second(10));
  };
  const handleStartStop = () => {
    setStatus((pre) => !pre);
  };

  useEffect(() => {
    setTimer((pre) => {
      if (pre.format("mm:ss") === "00:00") {
        return dayjs().minute(0).second(10);
      }
      return pre;
    });
  }, [timer]);

  useEffect(() => {
    if (!status) {
      const intervalId = setInterval(() => {
        setTimer((pre) => {
          return pre.subtract(1, "second");
        });
      }, 1000);
      return () => {
        clearInterval(intervalId);
      };
    }
  }, [status]);

  return (
    <>
      <div id="timer-label">
        count down
        <div id="time-left">{timer.format("mm:ss")}</div>
        <div>
          <button id="start_stop" onClick={() => handleStartStop()}>
            {status ? "start" : "stop"}
          </button>
          <button id="reset" onClick={handleReset}>
            reset
          </button>
        </div>
      </div>
    </>
  );
}

【问题讨论】:

  • 我刚刚运行了您的沙箱,倒计时达到“00:00”,然后从“00:10”重新开始。你能澄清一下问题是什么吗?预期的结果是什么?如果您想避免点击“00:00”,那么您的条件应该是if (pre.format("mm:ss") === "00:01") {
  • 你能澄清更多你的查询吗?
  • 我已经更改了我的沙箱。我不明白这个版本为什么不能按我的预期工作。 @AnilLoutombam
  • 我看到了同样的行为。有什么改变吗?据我所知,您的代码运行正常。如果你想让它做其他事情,那么你需要更好地描述你的期望。
  • 你能不能说的更具体一点,有什么问题

标签: reactjs react-hooks use-effect


【解决方案1】:

问题

当您拆分并使用 2 个 useEffect 挂钩时,效果与检查“00:00”的时间之间似乎存在一些时钟偏差,并且重置与间隔不同步。

useEffect(() => console.log('----- render -----'));

useEffect(() => {
  setTimer((pre) => {
    console.log('effect 2', pre.format("mm:ss"))
    if (pre.format("mm:ss") === "00:00") {
      return dayjs().minute(0).second(10);
    }
    return pre;
  });
}, [timer]);

useEffect(() => {
  if (!status) {
    const intervalId = setInterval(() => {
      setTimer((pre) => {
        console.log('effect 1', pre.format("mm:ss"))
        return pre.subtract(1, "second");
      });
    }, 1000);
    return () => {
      clearInterval(intervalId);
    };
  }
}, [status]);

这些日志产生:

----- render ----- 
effect 1 00:10 
----- render ----- 
effect 2 00:09 
effect 1 00:09 
----- render ----- 
effect 2 00:08 
effect 1 00:08 
----- render ----- 
effect 2 00:07 
----- render ----- 
effect 2 00:06 
effect 1 00:07 
----- render ----- 
effect 2 00:05 
effect 1 00:06 
----- render ----- 
effect 2 00:04 
effect 1 00:05 
----- render ----- 
effect 2 00:03 
effect 1 00:04 
----- render ----- 
effect 2 00:02 
effect 1 00:03 
----- render ----- 
effect 2 00:01 
effect 1 00:02 
----- render ----- 
effect 2 00:00  // <-- this one is also not in sync
----- render ----- 
effect 2 00:10  // <-- immediately reset 
effect 1 00:01 

通过将所有逻辑放在单个 useEffect 中解决此问题和偏差。

useEffect(() => {
  if (!status) {
    const intervalId = setInterval(() => {
      setTimer((pre) => {
        if (pre.format("mm:ss") === "00:00") {
          return dayjs().minute(0).second(10);
        }
        return pre.subtract(1, "second");
      });
    }, 1000);
    return () => {
      clearInterval(intervalId);
    };
  }
}, [status]);

【讨论】:

    【解决方案2】:

    每次计时器达到 00:00 时,您立即在第一次 useEffect 时将其更改为 00:10,而无需等待 1000 毫秒

    如果你想显示 00:00,你可以去掉上面的 useEffect 并检查你是否点击了 00:00

    import "./styles.css";
    import { useState, useEffect } from "react";
    import dayjs from "dayjs";
    
    export default function App() {
      const [timer, setTimer] = useState(dayjs().minute(0).second(10));
      const [status, setStatus] = useState(true);
    
      const handleReset = () => {
        setStatus(true);
        setTimer(dayjs().minute(0).second(10));
      };
      const handleStartStop = () => {
        setStatus((pre) => !pre);
      };
    
      useEffect(() => {
        if (!status) {
          const intervalId = setInterval(() => {
            setTimer((pre) => {
              if (pre.format("mm:ss") === "00:00")
                return dayjs().minute(0).second(10);
              return pre.subtract(1, "second");
            });
          }, 1000);
          return () => {
            clearInterval(intervalId);
          };
        }
      }, [status]);
    
      return (
        <>
          <div id="timer-label">
            count down
            <div id="time-left">{timer.format("mm:ss")}</div>
            <div>
              <button id="start_stop" onClick={() => handleStartStop()}>
                {status ? "start" : "stop"}
              </button>
              <button id="reset" onClick={handleReset}>
                reset
              </button>
            </div>
          </div>
        </>
      );
    }
    
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-11-25
      • 2021-07-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多