为什么 clearInterval 没有停止我的计时器?
Why is clearInterval not stopping my timer?
我正在尝试构建一个具有三个按钮的计时器,一个是开始按钮,一个是停止按钮,它在当前整数处停止,还有一个重置按钮。我在下面添加了导致以下问题的代码。
我以为我的停止功能会阻止计时器递减,但它会继续这样做。此外,当在控制台中记录我的计时器状态时,您可以看到它没有在控制台中更新,即使它在 DOM 中更新。这是为什么?
感谢您的任何见解。
import React from 'react';
import './style.css';
export default function App() {
const [timer, setTimer] = React.useState(50);
const reset = () => {
setTimer(50);
};
const start = () => {
setTimer((prev) => prev - 1);
};
// const interval = setInterval(() => {
// console.log(updated)
// //start() }, 1000)
// }
const interval = () => {
setInterval(() => {
console.log('updated');
console.log(timer);
start();
}, 1000);
};
const stop = () => {
clearInterval(start);
};
return (
<div>
<h1>{timer}</h1>
<button onClick={interval}>start</button>
<button onClick={stop}>stop</button>
<button onClick={reset}>reset</button>
</div>
);
}`
我认为您不应该将 setTimeout 包装到可调用对象中。然后你就失去了启动和停止它的能力,因为变量不引用间隔而是引用包装间隔的可调用对象。
看看这个指南:https://www.w3schools.com/jsref/met_win_clearinterval.asp
您在为间隔分配实际值时遇到了一个小问题。
这是它应该如何使用
const interval = setInterval(() => {})
clearInterval(interval)
对于您的代码更改,您可以创建一个 ref 来保留区间变量,并在以后使用它来清理区间。
function App() {
const [timer, setTimer] = React.useState(5);
const intervalRef = React.useRef(); //create a ref for interval
const reset = () => {
setTimer(5);
};
const start = () => {
setTimer((prev) => {
if(prev === 0) {
stop();
return 0;
}
return prev - 1;
});
};
// const interval = setInterval(() => {
// console.log(updated)
// //start() }, 1000)
// }
const interval = () => {
//assign interval ref here
intervalRef.current = setInterval(() => {
start();
}, 1000);
};
const stop = () => {
//clear the interval ref
clearInterval(intervalRef.current);
};
return (
<div>
<h1>{timer}</h1>
<button onClick={interval}>start</button>
<button onClick={stop}>stop</button>
<button onClick={reset}>reset</button>
</div>
);
}
ReactDOM.render(
<App/>,
document.getElementById("root")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.production.min.js"></script>
<div id="root"></div>
clearTimeout
或 clearInterval
各取一个令牌,该令牌由先前调用 setTimeout
或 setInterval
返回。所以您需要存储该令牌:
const id = setInterval(() => console.log("triggered"), 1000);
// ...
clearInterval(id)
此外,你应该注意如果 App
是 re-rendered 会发生什么,所以你应该将 set
/clear
逻辑放在 useEffect
这样你就可以清理你的间隔了。
此外,虽然您没有询问,但您的 console.log(timer)
不会起作用,并且将始终打印 50。该回调中的 timer
变量被捕获一次,并且是从未更新,因为该回调现在就在 setInterval
内。每次 App
re-renders 时,您都需要使用更新的回调函数清除并重置间隔,或者使用您不断更新的 ref,这很痛苦。
我建议借用这个为您考虑所有这些事情的自定义挂钩:https://usehooks-ts.com/react-hook/use-interval
那么你的 App
组件会变得非常简单,但仍然很健壮:
const { useEffect, useRef, useState, useLayoutEffect } = React;
// https://usehooks-ts.com/react-hook/use-interval
function useInterval(callback: () => void, delay: number | null) {
const savedCallback = useRef(callback);
useLayoutEffect(() => {
savedCallback.current = callback;
}, [callback])
useEffect(() => {
if (!delay && delay !== 0) return;
const id = setInterval(() => savedCallback.current(), delay);
return () => clearInterval(id);
}, [delay]);
}
function App() {
const [timer, setTimer] = useState(50);
const [running, setRunning] = useState(false);
useInterval(() => setTimer(t => t - 1), running ? 1000 : null);
const start = () => setRunning(true);
const stop = () => setRunning(false);
const reset = () => { setTimer(50); };
return (
<div>
<h1>{timer}</h1><button onClick={start}>start</button>
<button onClick={stop}> stop </button>
<button onClick={reset}> reset </button>
</div>
);
}
ReactDOM.render(<App />, document.getElementById("react"));
<div id="react"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
我正在尝试构建一个具有三个按钮的计时器,一个是开始按钮,一个是停止按钮,它在当前整数处停止,还有一个重置按钮。我在下面添加了导致以下问题的代码。
我以为我的停止功能会阻止计时器递减,但它会继续这样做。此外,当在控制台中记录我的计时器状态时,您可以看到它没有在控制台中更新,即使它在 DOM 中更新。这是为什么?
感谢您的任何见解。
import React from 'react';
import './style.css';
export default function App() {
const [timer, setTimer] = React.useState(50);
const reset = () => {
setTimer(50);
};
const start = () => {
setTimer((prev) => prev - 1);
};
// const interval = setInterval(() => {
// console.log(updated)
// //start() }, 1000)
// }
const interval = () => {
setInterval(() => {
console.log('updated');
console.log(timer);
start();
}, 1000);
};
const stop = () => {
clearInterval(start);
};
return (
<div>
<h1>{timer}</h1>
<button onClick={interval}>start</button>
<button onClick={stop}>stop</button>
<button onClick={reset}>reset</button>
</div>
);
}`
我认为您不应该将 setTimeout 包装到可调用对象中。然后你就失去了启动和停止它的能力,因为变量不引用间隔而是引用包装间隔的可调用对象。
看看这个指南:https://www.w3schools.com/jsref/met_win_clearinterval.asp
您在为间隔分配实际值时遇到了一个小问题。
这是它应该如何使用
const interval = setInterval(() => {})
clearInterval(interval)
对于您的代码更改,您可以创建一个 ref 来保留区间变量,并在以后使用它来清理区间。
function App() {
const [timer, setTimer] = React.useState(5);
const intervalRef = React.useRef(); //create a ref for interval
const reset = () => {
setTimer(5);
};
const start = () => {
setTimer((prev) => {
if(prev === 0) {
stop();
return 0;
}
return prev - 1;
});
};
// const interval = setInterval(() => {
// console.log(updated)
// //start() }, 1000)
// }
const interval = () => {
//assign interval ref here
intervalRef.current = setInterval(() => {
start();
}, 1000);
};
const stop = () => {
//clear the interval ref
clearInterval(intervalRef.current);
};
return (
<div>
<h1>{timer}</h1>
<button onClick={interval}>start</button>
<button onClick={stop}>stop</button>
<button onClick={reset}>reset</button>
</div>
);
}
ReactDOM.render(
<App/>,
document.getElementById("root")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.production.min.js"></script>
<div id="root"></div>
clearTimeout
或 clearInterval
各取一个令牌,该令牌由先前调用 setTimeout
或 setInterval
返回。所以您需要存储该令牌:
const id = setInterval(() => console.log("triggered"), 1000);
// ...
clearInterval(id)
此外,你应该注意如果 App
是 re-rendered 会发生什么,所以你应该将 set
/clear
逻辑放在 useEffect
这样你就可以清理你的间隔了。
此外,虽然您没有询问,但您的 console.log(timer)
不会起作用,并且将始终打印 50。该回调中的 timer
变量被捕获一次,并且是从未更新,因为该回调现在就在 setInterval
内。每次 App
re-renders 时,您都需要使用更新的回调函数清除并重置间隔,或者使用您不断更新的 ref,这很痛苦。
我建议借用这个为您考虑所有这些事情的自定义挂钩:https://usehooks-ts.com/react-hook/use-interval
那么你的 App
组件会变得非常简单,但仍然很健壮:
const { useEffect, useRef, useState, useLayoutEffect } = React;
// https://usehooks-ts.com/react-hook/use-interval
function useInterval(callback: () => void, delay: number | null) {
const savedCallback = useRef(callback);
useLayoutEffect(() => {
savedCallback.current = callback;
}, [callback])
useEffect(() => {
if (!delay && delay !== 0) return;
const id = setInterval(() => savedCallback.current(), delay);
return () => clearInterval(id);
}, [delay]);
}
function App() {
const [timer, setTimer] = useState(50);
const [running, setRunning] = useState(false);
useInterval(() => setTimer(t => t - 1), running ? 1000 : null);
const start = () => setRunning(true);
const stop = () => setRunning(false);
const reset = () => { setTimer(50); };
return (
<div>
<h1>{timer}</h1><button onClick={start}>start</button>
<button onClick={stop}> stop </button>
<button onClick={reset}> reset </button>
</div>
);
}
ReactDOM.render(<App />, document.getElementById("react"));
<div id="react"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>