import React , {useState, useEffect} from 'react';
const Timer = () =>{
const [timer, setTimer] = useState(120);
const countTimer = () =>{
if(timer <= 0){
localStorage.clear("timer");
console.log("timer less then 0")
return;
} else {
console.log("greater then 0")
setTimer(timer -1) ;
localStorage.setItem("timer",timer);
setTimeout(countTimer(),1000);
}
}
useEffect(()=>{
if(localStorage.getItem("timer")){
setTimer(localStorage.getItem("timer"));
} else {
setTimer(120);
}
if(timer){
setTimeout(countTimer(),1000);
}
},[timer])
return (
<div align="center">
Timer :{timer}
</div>
)
}
export default Timer
我的目标是在反应中实现倒数计时器,它将与本地存储和跨浏览器选项卡同步,页面显示计数器, 想要同步状态定时器和本地存储定时器,把它们放在 useEffect 定时器更改状态更新定时器和更新本地存储内部 setTimeout ,拜托!帮帮我,谢谢
答案 0 :(得分:0)
您的代码存在一些问题。
第一个问题是 setTimeout
接受回调函数作为它的第一个参数。它将在作为第二个参数提供的时间之后执行。但不是将函数 countTimer
作为回调传递,而是传递它的执行结果。
if(timer){
setTimeout(countTimer(),1000); // here you're passing as the callback
// the result of execution of countTimer
// namely `undefined` value
}
另一个问题是您调用了 setTimeout
两次。每次 useEffect
变量更改时都会执行的 timer
钩子内部以及每次执行时的 countTimer
函数内部。
还有一个问题。每次当 timer
变量更改时,您都会从 timer
读取 localStorage
键并再次设置 setTimer
。当 state
被 useEffect
改变时,它会导致 React 变为 schedule another render。并且您的代码将触发几乎无限循环。这将不断地重新渲染组件,直到时间用完。
还有一个问题。如果您的组件在超时仍在运行时被卸载。挂起的函数最终将被执行。它会将卸载前的 timer
值写入 localStorage。如果你再次挂载这个组件,执行时挂起的超时可能会覆盖新创建的值。
这是清除任何挂起的超时或其他对卸载的影响的好习惯。所以你最好清理 setTimeout 如果它仍然运行。为此,您必须返回 cleanup
函数作为 useEffect
钩子的返回值。
总结:
setTimeout
,而不是它的执行结果useEffect
内触发无休止的重新渲染总而言之,它应该是这样的:
import * as React from 'react'
const Timer = () => {
const initialTimer = localStorage.getItem("timer") ?? 120;
const timeoutId = React.useRef(null);
const [timer, setTimer] = React.useState(initialTimer);
const countTimer = React.useCallback(() => {
if (timer <= 0) {
localStorage.clear("timer");
} else {
setTimer(timer - 1);
localStorage.setItem("timer", timer);
}
}, [timer]);
React.useEffect(() => {
timeoutId.current = window.setTimeout(countTimer, 1000);
// cleanup function
return () => window.clearTimeout(timeoutId.current);
}, [timer, countTimer]);
return <div align="center">Timer :{timer}</div>;
}
export default Timer