[英]clearInterval not working in React Application using functional component
我想使用functional component
在React
中構建一個計時器應用程序,以下是要求。
該組件將顯示一個初始化為0
的數字,稱為counter
。
該組件將在counter
編號下方顯示一個Start
按鈕。
單擊“ Start
”按鈕后,計數器將開始運行。 這意味着counter
編號將開始每 1 秒遞增 1。
當計數器運行(遞增)時, Start
按鈕將變為Pause
按鈕。
單擊Pause
按鈕時, counter
將保留其值(數字)但停止運行(遞增)。
該組件還將顯示一個Reset
按鈕。 單擊Reset
按鈕后, counter
將 go 恢復為其初始值(在我們的示例中為0
)並停止運行(遞增)。
下面是我已經實現的代碼,但clearInterval
似乎沒有工作,另外我如何實現重置按鈕?
代碼:
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 鏈接: CodeSandbox
我做了一個稍微不同的版本,它使用了一個在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>
);
}
演示:https://codesandbox.io/s/dank-night-wwxqz3?file=/src/Counter.js
作為額外的一點,我制作了一個使用自定義鈎子useTimer
的版本。 這樣,組件代碼就更干凈了: https://codesandbox.io/s/agitated-curie-nkjf62?file=/src/Counter.js
使用useRef
將區間作為參考。 然后使用resetCounter()
清理間隔參考。
const intervalRef = useRef(null)
const incrementCounter = () => {
intervalRef.current = setInterval(() => {
setCounter(prevState => prevState + 1)
}, 1000);
};
const resetCounter = () => {
clearInterval(intervalRef.current);
intervalRef.current = null;
};
您必須將myInterval
存儲在 state 中。 之后,當單擊按鈕且flag
為false
時,您可以清除間隔(狀態為 myInterval)。
在每次渲染之間,您的變量myInterval
值無法生存。 這就是為什么您需要使用 [ useRef
][1] 鈎子在每次渲染中保存此變量的引用。
此外,您不需要標志 function,因為您擁有myClicked
變量的所有信息
這是使用這些修改對您的代碼進行的修改。 如果您有任何問題,請不要猶豫。
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
我會把這個留給有同樣問題的人。
就我而言,問題是使用節點 setInterval 而不是 window.setInterval。
這是一個問題,因為這會返回一個 Node.Timer 類型,它是 object 而不是數字(setInterval ID),因為 clearInterval() 需要一個參數類型的數字才能工作。 所以要解決這個問題,
React.useEffect(() => {
let timeoutId;
timeoutId = window.setInterval(callback, 100);
return = () => {
if(timeoutId) clearInterval(timeoutId)
}
}, [])
或在 class 組件中使用 componentWillMount()
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.