【问题标题】:setInterval start and stop behaviorsetInterval 开始和停止行为
【发布时间】:2020-08-18 05:10:39
【问题描述】:

我正在创建一个秒表,它有一个开始和停止时间的按钮,但我遇到了 setInterval 行为问题。

在 React 的功能组件级别声明时,它会在安装后运行。
示例:

const Timer = () => {
    const timer = setInterval(() => console.log('running'), 1000)
}

当我在函数中声明它时,在调用该函数之前它不会运行,但我无法让它停止。

const Timer = () => {
    const [start, setStart] = useState(false)

    const startStopTimer = () => {
        const timer = setInterval(() => console.log('running'), 1000)
    }

    return (<Button 
                onClick={() => {
                   setStarted(!start)
                   startStopTimer()
                }
             > Start/Stop </Button>)
}

然后我尝试在函数中添加clearInterval(),如果start === false 有条件地调用它。在这个实现中,第一个按钮点击什么都不做。第二次点击启动计时器,但无法停止。

const Timer = () => {
    const [start, setStart] = useState(false)

    const startStopTimer = () => {
        let timer = setInterval(() => console.log('running'), 1000)
        if (!started) clearInterval(timer)
    }

    return (<Button 
                onClick={() => {
                   setStarted(!start)
                   startStopTimer()
                }
             > Start/Stop </Button>)
}

【问题讨论】:

  • 因为你一直在创建变量 timer。

标签: javascript reactjs setinterval


【解决方案1】:

Ciao,我建议你这样修改你的代码:

const Timer = () => {
    //const [start, setStart] = useState(false) do not use state for scripting reasons
    let timer = null;

    const startStopTimer = () => {
        if(!timer) timer = setInterval(() => console.log('running'), 1000)
        else {
           clearInterval(timer)
           timer = null
        } 
    }

    return (<Button 
                onClick={() => {
                   //setStarted(!start)
                   startStopTimer()
                }
             > Start/Stop </Button>)
}

说明:timer 应该定义在startStopTimer 之外,否则每次启动startStopTimer 时,都会创建一个新的timer

好的,这很容易,但现在是重要的部分:我强烈建议您不要出于脚本原因使用反应状态。反应状态应该仅用于渲染原因。为什么?因为钩子是异步的,如果您在使用 setStart 后立即读取 start,您将读取旧值。

【讨论】:

  • 我刚试过这个方法,它可以用来启动计时器。然而它并没有停止。每次单击时,它都会添加另一个 setInterval 实例。例如,第一次单击会导致 console.log('running') 每秒一次。第二次点击日志每秒“运行”两次,依此类推。
  • 对不起@SimonWong 我忘了把timer = setInterval(() =&gt; console.log('running'), 1000)...现在试试。应该工作。
  • 谢谢!但是现在一旦停止,就不会再开始了。我在 else 语句中添加了timer = null,它现在可以完美运行了!请编辑您的答案以包括:)
  • 我刚刚被提醒为什么我需要使用useState。每次调用 setInterval 时,我都想用新值更新 UI。但就像你说的,如果我使用let [seconds, setSeconds] = useState(0) 并多次单击按钮,它会多次调用 setInterval。知道我应该如何处理这个问题吗?
  • @SimonWong 但是如果调用了 setInterval,您只需要在 UI 上进行可视化,对吗?我的意思是你想在 UI 中看到 1 或 0
【解决方案2】:

timer 变量是本地变量,每次调用 sartStopTimer() 时它只在 sartStopTimer() 范围内生成新的 timer,并且在清除超时时,您只是清除最近生成的而不是以前的 timer

let timer;
const startStopTimer = () => {
        timer = setInterval(() => console.log('running'), 1000)
        if (!started) clearInterval(timer)
    }

制作timer全局变量。

这应该让你开始。

【讨论】:

    【解决方案3】:

    试试这个:

    const Timer = () => {
        const [timerId, setTimerId] = useState(null)
            
        function startStopTimer(){
          if (timerId) {
            clearInterval(timerId)
            setTimerId(null)
          } else {
            setTimerId(setInterval(() => console.log('hello'), 1000))
          }
        }
        return <button onClick={startStopTimer}> Start/Stop </button>
    }
    

    顺便说一句,不需要使用状态。如果您不需要保持计时器状态,当然可以使用普通变量。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-22
      • 1970-01-01
      • 2010-12-29
      • 1970-01-01
      • 2020-08-17
      • 1970-01-01
      相关资源
      最近更新 更多