使用挂钩同步状态倒计时与本地存储的反应组件
react component using hooks sync state count down time with local storage
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
我的目标是在 React 中实现倒数计时器,它将与本地存储同步并跨浏览器选项卡与显示计数器的页面同步,
想同步状态定时器和本地存储定时器,把它们放在 setTimeout 里面使用定时器改变更新状态定时器和更新本地存储的效果,拜托!帮帮我,谢谢
您的代码存在一些问题。
第一个问题是 setTimeout
接受回调函数作为第一个参数。它将在作为第二个参数提供的时间之后执行。但不是传递函数 countTimer
作为回调,而是传递它的执行结果。
if(timer){
setTimeout(countTimer(),1000); // here you're passing as the callback
// the result of execution of countTimer
// namely `undefined` value
}
另一个问题是您两次调用 setTimeout
。每次 timer
变量更改时将执行的内部 useEffect
挂钩以及每次执行时内部 countTimer
函数。
还有一个问题。每次 timer
变量发生变化时,您都会从 localStorage
读取 timer
键并再次设置 setTimer
。当 state
被 useEffect
改变时,它会导致 React 变为 schedule another render。而且您的代码将触发几乎无休止的循环。这将不断重新渲染组件,直到时间用完。
还有一个问题。如果您的组件在超时仍在运行时被卸载。挂起的功能最终将被执行。它会将卸载前的 timer
值写入 localStorage。如果您再次挂载此组件,执行时的挂起超时可能会覆盖新创建的值。
清除任何挂起的超时或对卸载的其他影响是一个好习惯。因此,如果 setTimeout 仍在运行,您最好清除它。为此,您必须 return cleanup
作为 useEffect
钩子中的 return 值。
总结:
- 将回调函数传递给
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
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
我的目标是在 React 中实现倒数计时器,它将与本地存储同步并跨浏览器选项卡与显示计数器的页面同步, 想同步状态定时器和本地存储定时器,把它们放在 setTimeout 里面使用定时器改变更新状态定时器和更新本地存储的效果,拜托!帮帮我,谢谢
您的代码存在一些问题。
第一个问题是 setTimeout
接受回调函数作为第一个参数。它将在作为第二个参数提供的时间之后执行。但不是传递函数 countTimer
作为回调,而是传递它的执行结果。
if(timer){
setTimeout(countTimer(),1000); // here you're passing as the callback
// the result of execution of countTimer
// namely `undefined` value
}
另一个问题是您两次调用 setTimeout
。每次 timer
变量更改时将执行的内部 useEffect
挂钩以及每次执行时内部 countTimer
函数。
还有一个问题。每次 timer
变量发生变化时,您都会从 localStorage
读取 timer
键并再次设置 setTimer
。当 state
被 useEffect
改变时,它会导致 React 变为 schedule another render。而且您的代码将触发几乎无休止的循环。这将不断重新渲染组件,直到时间用完。
还有一个问题。如果您的组件在超时仍在运行时被卸载。挂起的功能最终将被执行。它会将卸载前的 timer
值写入 localStorage。如果您再次挂载此组件,执行时的挂起超时可能会覆盖新创建的值。
清除任何挂起的超时或对卸载的其他影响是一个好习惯。因此,如果 setTimeout 仍在运行,您最好清除它。为此,您必须 return cleanup
作为 useEffect
钩子中的 return 值。
总结:
- 将回调函数传递给
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