有一个原因,为什么我的clearInterval函数不工作在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;然后像interval = setInterval(countdown, 1000);那样给它分配setInterval

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

你需要做的是创建一个变量,它不会在重新渲染器中改变,你可以使用useRef

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

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

在这里找到一个演示

基本上,当功能组件重新渲染时,它将从上到下执行,如果你使用let counter = 0;,那么在每次重新渲染时,它将初始化为0,如果你需要在每次重新渲染中持久化你的值,你可能需要一些钩子(例如: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>

我希望这是有用的

最新更新