简体   繁体   English

setInterval 只执行一次

[英]setInterval only executes once

I'm working on a React component where on form submit, a timer takes in the value input from a slider component and starts to count down to zero.我正在处理 React 组件,在表单提交时,计时器接收来自 slider 组件的值输入并开始倒计时到零。 However, it is only the timer stops after 1 of the setInterval function in triggerClock().但是,它只是在triggerClock()中的setInterval function的1之后定时器停止。 I'm not sure what to do.我不知道该怎么办。

My React component:我的 React 组件:

const Home = (props) => {
  let timeInMinutes;
  let timeInSeconds;

  const [time, setTime] = useState(0);
  const [minutes, setMinutes] = useState(0);
  const [seconds, setSeconds] = useState(0);

  const handleTimeChange = (event) => {
    setTime(event.target.value);
  };

  const triggerClock = () => {
    const interval = setInterval(() => {
      if (minutes === 0 && seconds === 0) {
        clearInterval(interval);
      }

      if (seconds === 0) {
        setSeconds((sec) => sec + 59);
        setMinutes((min) => min - 1);
      } else {
        setSeconds((sec) => sec - 1);
      }
    }, 1000);

    return () => {
      clearInterval(interval);
    };
  };

  const handleFormSubmit = (event) => {
    event.preventDefault();
    timeInMinutes = time;
    timeInSeconds = timeInMinutes * 60;
    setMinutes(timeInSeconds / 60);
    setSeconds(timeInSeconds % 60);
    triggerClock();
  };

  return (
    <React.Fragment>
      <Header />
      <Container maxWidth="xs" sx={{ textAlign: "center" }}>
        <Typography variant="h5">Set Time (Minutes)</Typography>
        <Box component="form" onSubmit={handleFormSubmit}>
          <Slider
            aria-label="Timer"
            valueLabelDisplay="auto"
            step={10}
            marks
            min={0}
            max={60}
            value={time}
            onChange={handleTimeChange}
          />
          <Button type="submit">Submit</Button>
        </Box>
        <div>
          {minutes}:{seconds}
        </div>
      </Container>
    </React.Fragment>
  );
};

There are a few issues here;这里有几个问题; first I'd suggest you have a read of Dan Abramov's excellent Making setInterval declarative blog post which is a great way to get your head around the tricky intersection of setInterval and React Hooks.首先,我建议您阅读 Dan Abramov 的优秀Making setInterval declarative博客文章,这是让您了解setInterval和 React Hooks 的棘手交集的好方法。

Firstly, you're putting too much stuff into React state. Your minutes and seconds can be directly, and trivially, derived from time .首先,你在 React state 中放入了太多东西。你的minutesseconds可以直接且简单地从time派生。 We don't need React to look after them for us, they are relevant only during "this render".我们不需要 React 为我们照顾它们,它们仅在“此渲染”期间相关。 so:所以:

  const [time, setTime] = useState(0);

  const minutes = Math.floor(time / 60);
  const seconds = time % 60;

Secondly, in the spirit of Dan's quote:其次,本着丹引述的精神:

"we can describe the process at all points in time simultaneously" “我们可以同时描述所有时间点的过程”

we need to know, and control, whether the timer is counting down or not:我们需要知道并控制计时器是否正在倒计时:

const [timerRunning, setTimerRunning] = useState(false);

...

const triggerClock = () => {
  setTimerRunning(true);
};

Note that an additional benefit of this is we can now make our GUI reflect the state:请注意,这样做的另一个好处是我们现在可以让我们的 GUI 反映 state:

<Button disabled={timerRunning} type="submit">Submit</Button>

And finally, we have all the necessary parts to write a useEffect that will encompass the setInterval with the desired countdown, stop at the right time and clean up the interval:最后,我们拥有编写useEffect的所有必要部分,它将包含setInterval和所需的倒计时,在正确的时间停止清理间隔:

  useEffect(() => {
    if (timerRunning) {
      const interval = setInterval(() => {
        setTime((oldTime) => {
          const newTime = oldTime - 1;
          if (newTime === 0) {
            setTimerRunning(false);
          }
          return newTime;
        });
      }, 1000);
      return () => {
        clearInterval(interval);
      };
    }
  }, [timerRunning]);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM