[英]clearInterval not working in React Application using functional component
I wanted to build a timer application in React
using functional component
and below are the requirements.我想使用
functional component
在React
中构建一个计时器应用程序,以下是要求。
The component will display a number initialized to 0
know as counter
.该组件将显示一个初始化为
0
的数字,称为counter
。
The component will display a Start
button below the counter
number.该组件将在
counter
编号下方显示一个Start
按钮。
On clicking the Start
button the counter will start running.单击“
Start
”按钮后,计数器将开始运行。 This means the counter
number will start incrementing by 1 for every one second.这意味着
counter
编号将开始每 1 秒递增 1。
When the counter is running(incrementing), the Start
button will become the Pause
button.当计数器运行(递增)时,
Start
按钮将变为Pause
按钮。
On clicking the Pause
button, the counter
will preserve its value (number) but stops running(incrementing).单击
Pause
按钮时, counter
将保留其值(数字)但停止运行(递增)。
The component will also display a Reset
button.该组件还将显示一个
Reset
按钮。 On clicking the Reset
button, the counter
will go to its initial value(which is 0
in our case) and stops running(incrementing).单击
Reset
按钮后, counter
将 go 恢复为其初始值(在我们的示例中为0
)并停止运行(递增)。
Below is the code that I have implemented, but clearInterval
doesn't seems to be working, Also how do i implement Reset Button?下面是我已经实现的代码,但
clearInterval
似乎没有工作,另外我如何实现重置按钮?
Code:代码:
import React, { useState, useEffect } from "react";
export default function Counter() {
const [counter, setCounter] = useState(0);
const [flag, setFlag] = useState(false);
const [isClicked, setClicked] = useState(false);
var myInterval;
function incrementCounter() {
setClicked(!isClicked);
if (flag) {
myInterval = setInterval(
() => setCounter((counter) => counter + 1),
1000
);
setFlag(false);
} else {
console.log("sasdsad");
clearInterval(myInterval);
}
}
function resetCounter() {
clearInterval(myInterval);
setCounter(0);
}
useEffect(() => {
setFlag(true);
}, []);
return (
<div>
<p>{counter}</p>
<button onClick={incrementCounter}>
{isClicked ? "Pause" : "Start"}
</button>
<button onClick={resetCounter}>Reset</button>
</div>
);
}
Codesandbox link: CodeSandbox Codesandbox 链接: CodeSandbox
I did a slightly different version that use an extra useEffect
that runs on isRunning
(changed name from flag
) change:我做了一个稍微不同的版本,它使用了一个在
isRunning
上运行的额外useEffect
(从flag
更改了名称)更改:
import React, { useState, useEffect, useRef } from "react";
export default function Counter() {
const [counter, setCounter] = useState(0);
// Change initial value to `false` if you don't want
// to have timer running on load
// Changed `flag` name to more significant name
const [isRunning, setIsRunning] = useState(false);
// You don't need 2 variable for this
//const [isClicked, setClicked] = useState(false);
// Using `useRef` to store a reference to the interval
const myInterval = useRef();
useEffect(() => {
// You had this line to start timer on load
// but you can just set the initial state to `true`
//setFlag(true);
// Clear time on component dismount
return () => clearInterval(myInterval.current);
}, []);
// useEffect that start/stop interval on flag change
useEffect(() => {
if (isRunning) {
myInterval.current = setInterval(
() => setCounter((counter) => counter + 1),
1000
);
} else {
clearInterval(myInterval.current);
myInterval.current = null;
}
}, [isRunning]);
// Now on click you only change the flag
function toggleTimer() {
setIsRunning((isRunning) => !isRunning);
}
function resetCounter() {
clearInterval(myInterval.current);
myInterval.current = null;
setCounter(0);
setIsRunning(false);
}
return (
<div>
<p>{counter}</p>
<button onClick={toggleTimer}>{isRunning ? "Pause" : "Start"}</button>
<button onClick={resetCounter}>Reset</button>
</div>
);
}
Demo:https://codesandbox.io/s/dank-night-wwxqz3?file=/src/Counter.js演示:https://codesandbox.io/s/dank-night-wwxqz3?file=/src/Counter.js
As a little extra i've made a version that uses a custom hook useTimer
.作为额外的一点,我制作了一个使用自定义钩子
useTimer
的版本。 In this way the component code is way cleaner: https://codesandbox.io/s/agitated-curie-nkjf62?file=/src/Counter.js这样,组件代码就更干净了: https://codesandbox.io/s/agitated-curie-nkjf62?file=/src/Counter.js
Use useRef
to make the interval as a ref.使用
useRef
将区间作为参考。 Then use resetCounter()
to clean the interval ref.然后使用
resetCounter()
清理间隔参考。
const intervalRef = useRef(null)
const incrementCounter = () => {
intervalRef.current = setInterval(() => {
setCounter(prevState => prevState + 1)
}, 1000);
};
const resetCounter = () => {
clearInterval(intervalRef.current);
intervalRef.current = null;
};
You have to store myInterval
in state.您必须将
myInterval
存储在 state 中。 After that when button is clicked and flag
is false
, you can clear interval (myInterval in state).之后,当单击按钮且
flag
为false
时,您可以清除间隔(状态为 myInterval)。
Between each rendering your variable myInterval
value doesn't survive.在每次渲染之间,您的变量
myInterval
值无法生存。 That's why you need to use the [ useRef
][1] hook that save the reference of this variable across each rendering.这就是为什么您需要使用 [
useRef
][1] 钩子在每次渲染中保存此变量的引用。
Besides, you don't need an flag function, as you have all information with the myClicked
variable此外,您不需要标志 function,因为您拥有
myClicked
变量的所有信息
Here is a modification of your code with those modifications.这是使用这些修改对您的代码进行的修改。 Don't hesitate if you have any question.
如果您有任何问题,请不要犹豫。
import React, { useState, useEffect, useRef } from "react";
export default function Counter() {
const [counter, setCounter] = useState(0);
const [isStarted, setIsStarted] = useState(false);
const myInterval = useRef();
function start() {
setIsStarted(true);
myInterval.current = setInterval(() => setCounter((counter) => counter + 1), 100);
100;
}
function pause() {
setIsStarted(false);
clearInterval(myInterval.current);
}
function resetCounter() {
clearInterval(myInterval.current);
setCounter(0);
}
return (
<div>
<p>{counter}</p>
{!isStarted ?
<button onClick={start}>
Start
</button>
:
<button onClick={pause}>
Pause
</button>
}
<button onClick={resetCounter}>Reset</button>
</div>
);
}
\\\
[1]: https://reactjs.org/docs/hooks-reference.html#useref
I'll just leave this here for anyone having the same problem.我会把这个留给有同样问题的人。
in my case, the issue was node setInterval was used instead of window.setInterval.就我而言,问题是使用节点 setInterval 而不是 window.setInterval。
this is a problem since this returns a type of Node.Timer which is an object instead of number (setInterval ID) for the clearInterval() to work as it needs an argument type of number.这是一个问题,因为这会返回一个 Node.Timer 类型,它是 object 而不是数字(setInterval ID),因为 clearInterval() 需要一个参数类型的数字才能工作。 so to fix this,
所以要解决这个问题,
React.useEffect(() => {
let timeoutId;
timeoutId = window.setInterval(callback, 100);
return = () => {
if(timeoutId) clearInterval(timeoutId)
}
}, [])
or in class components use componentWillMount()或在 class 组件中使用 componentWillMount()
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.