简体   繁体   English

我的 clearInterval function 不能在 React 中工作有什么原因吗?

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

I've created a simple timer script initialized with 10 seconds for testing.我创建了一个简单的计时器脚本,初始化时间为 10 秒以进行测试。 The problem I'm having is that the pause timer button isn't working as expected.我遇到的问题是暂停计时器按钮没有按预期工作。

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;

I want the pause button to pause the timer.我想要暂停按钮来暂停计时器。 Eventually I'll make conditional statements to have each button appear based on the state of the app and the value of time, but the pause button is my current obstacle.最终,我将根据应用程序的 state 和时间值制作条件语句,让每个按钮出现,但暂停按钮是我目前的障碍。

I first had a separate countdown function which used a conditional to stop the time when the time matched counter (below) .我首先有一个单独的倒计时 function ,它使用条件来停止时间匹配计数器的时间(如下) I thought of a less complicated way that lets me omit the counter variable (above) .我想到了一种不太复杂的方法,可以让我省略计数器变量(上图) Im not sure which option is better, or if either is preventing the clearInterval function to work properly.我不确定哪个选项更好,或者是否阻止clearInterval function 正常工作。 The clearInterval function works within the countdown function if statement, but will not work outside of it. clearInterval function 在倒计时 function 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;

Basically you can't create let interval;基本上你不能创建let interval; and assign it a setInterval like interval = setInterval(countdown, 1000);并为其分配一个 setInterval,如interval = setInterval(countdown, 1000);

because on each re-render there will be new let interval;因为每次重新渲染都会有新的let interval;

what you need to do is create a variable which isn't change on re-redners, you can use useRef您需要做的是创建一个不会在 re-redners 上更改的变量,您可以使用useRef

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

and I don't think you need const [timerRunning, setTimerRunning] = useState(false);而且我认为您不需要const [timerRunning, setTimerRunning] = useState(false);

find a demo here在这里找到一个演示

Basically when functional component re-renders it will execute from top to bottom, if you use like let counter = 0;基本上,当功能组件重新渲染时,它将从上到下执行,如果您使用 like let counter = 0; , then on each re-render it will initialize to 0, if you need to persists your values in each re-renders you might need some hooks (Ex: useState, useRef...), in this case useRef would do the trick (because you need only one setInterval in each re-renders and useRef will give you the previous value, it will not re-initalize like a general variable) ,然后在每次重新渲染时,它将初始化为 0,如果您需要在每次重新渲染中保留您的值,您可能需要一些钩子(例如:useState,useRef ...),在这种情况下, useRef可以解决问题(因为在每次重新渲染中只需要一个 setInterval 并且 useRef 会给你以前的值,它不会像一般变量一样重新初始化)

that is my solution那是我的解决方案

instead of the startTime function, I use useEffect而不是 startTime function,我使用 useEffect

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

and in onClick Start Button并在 onClick 启动按钮

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

I hope it is useful我希望它有用

You have to use useEffect, like this:你必须使用 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])

you have three buttons to start, pause and restart, invoke these (handleStart, handlePause, handleRestart) functions with them你有三个按钮来启动、暂停和重新启动,用它们调用这些(handleStart, handlePause, handleRestart)函数

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

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