为什么我的 clearInterval 函数在 React 中不起作用?

Is there a reason why my clearInterval function isnt working in React?

我创建了一个简单的定时器脚本,初始化为 10 秒用于测试。我遇到的问题是暂停计时器按钮没有按预期工作。

import React, { useState } from 'react';

function App() {

  const [time, updateTime] = useState(() => { return 10 });
  const [timerRunning, setTimerRunning] = useState(false);
  
  let minutes = Math.floor(time / 60);
  let seconds = time % 60;
  let interval;

  function startTimer() {
    setTimerRunning(true);
    interval = setInterval( function() {
      updateTime(previousTime => previousTime === 0 ? previousTime : previousTime - 1);
    }, 1000);
  }

  function pauseTimer() {
    setTimerRunning(false);
    clearInterval(interval);
  }

  function restartTimer() {
    setTimerRunning(false);
    updateTime(() => {return 10});
  }

  return (
    <>
      <p>{minutes > 9 ? minutes : "0" + minutes}:{seconds > 9 ? seconds : "0" + seconds}</p>
      <button onClick={startTimer}>Start</button>
      <button onClick={pauseTimer}>Pause</button>
      <button onClick={restartTimer}>Restart</button>
    </>
  )
}

export default App;

我想要暂停按钮来暂停计时器。最终我会根据应用程序的状态和时间值制作条件语句让每个按钮出现,但暂停按钮是我目前的障碍。

我首先有一个单独的倒计时功能,它使用条件在时间匹配计数器(下)时停止时间。我想到了一种不那么复杂的方法,可以让我省略计数器变量 (上图)。我不确定哪个选项更好,或者是否有任何一个阻止 clearInterval 函数正常工作。 clearInterval 函数在倒计时函数 if 语句中起作用,但在它之外不起作用。

import React, { useState } from 'react';

function App() {

  const [time, updateTime] = useState(() => { return 10 });
  const [timerRunning, setTimerRunning] = useState(false);
  
  let counter = 0;
  let minutes = Math.floor(time / 60);
  let seconds = time % 60;
  let interval;

  function countdown() {
    counter++;
    if ( counter === time ) {
       setTimerRunning(false);
       clearInterval(interval);
    }
      updateTime(previousTime => previousTime - 1);
  }
  

  function startTimer() {
    setTimerRunning(true);
    interval = setInterval(countdown, 1000);
  }

  function pauseTimer() {
    setTimerRunning(false);
    clearInterval(interval);
  }

  function restartTimer() {
    setTimerRunning(false);
    updateTime(() => {return 10});
  }



  return (
    <>
      <p>{minutes > 9 ? minutes : "0" + minutes}:{seconds > 9 ? seconds : "0" + seconds}</p>
      <button onClick={startTimer}>Start</button>
      <button onClick={pauseTimer}>Pause</button>
      <button onClick={restartTimer}>Restart</button>
    </>
  )
}

export default App;

基本上您不能创建 let interval; 并为其分配一个 setInterval,例如 interval = setInterval(countdown, 1000);

因为每次重新渲染都会有新的let interval;

您需要做的是创建一个变量,该变量不会在 re-redners 上更改,您可以使用 useRef

const interval = useRef(null);
.....
function startTimer() {
    interval.current = setInterval(countdown, 1000);
}
....
function pauseTimer() {
   clearInterval(interval.current);
}

我认为你不需要const [timerRunning, setTimerRunning] = useState(false);

找到一个demo here

基本上当功能组件重新渲染时它会从上到下执行,如果你像let counter = 0;那样使用,那么在每次重新渲染时它都会初始化为0,如果你需要持久化你的值每次重新渲染你可能需要一些 hooks(例如:useState,useRef ...),在这种情况下 useRef 就可以了(因为你只需要一个 setInterval每次重新渲染和 useRef 都会给你以前的值,它不会像一般变量那样重新初始化)

你必须使用useEffect,像这样:

 const handleStart = () => {
    setChangeValue(true)
  }

  const handlePause = () => {
    setChangeValue(false)
    pauseTimer()
  }

  const handleRestart = () => {
    setInitialState()
    setChangeValue(true)
  }

  useEffect(() => {
    if (changeValue) {
      const interval = setInterval(() => {
        startTimer()
      }, 100)
      return () => clearInterval(interval)
    }
  }, [changeValue])

您有启动、暂停和重新启动三个按钮,用它们调用这些 (handleStart, handlePause, handleRestart) 功能

这是我的解决方案

我使用 useEffect

而不是 startTime 函数
  useEffect(()=>{
    interval = timerRunning && setInterval(() => {
      updateTime(previousTime => previousTime === 0 ? previousTime : previousTime - 1);
    }, 1000);
    return ()=> clearInterval(interval)
  },[timerRunning])

并在 onClick 开始按钮

<button onClick={()=> setTimerRunning(true)}>Start</button>

希望有用