【问题标题】:react component using hooks sync state count down time with local storage使用钩子反应组件与本地存储同步状态倒计时
【发布时间】:2021-06-25 16:37:15
【问题描述】:

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 中实现倒计时计时器,该计时器将与本地存储和跨浏览器选项卡同步,页面显示计数器, 想要同步 state 的 timer 和 local storage 的 timer,把它们放在 useEffect 的 timer change 更新 state 的 timer 和更新 setTimeout 里面的 local storage,拜托了!帮帮我,谢谢

【问题讨论】:

  • 请为您所面临的问题提供更多背景信息,并正确格式化代码,以便其他人以更好的方式回答问题!
  • 在 react 中实现倒数计时器,它将与 localstorage 和跨浏览器选项卡同步,页面显示计数器请帮助

标签: javascript react-hooks local-storage event-listener


【解决方案1】:

您的代码存在一些问题。

第一个问题是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。当stateuseEffect 更改时,它会导致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

codesandbox link

【讨论】:

  • 非常感谢,但跨标签的计时器不同步,请您帮忙
  • 在代码沙箱中打开几个选项卡时,它看起来对我来说非常好。你能详细说明确切的问题吗?它可能会在超时期间不同步。但这是一个相当复杂的问题。您必须在选项卡之间分配leader-follower 关系,以使只有领导者写入本地存储,当领导者关闭时重新选举并分配另一个选项卡作为领导者。这远远超出了简单的 stackoverflow 答案。
  • 我想要两个定时器,一个是空闲定时器,另一个是弹出定时器,如果页面空闲定时器(10 秒)上没有活动应该触发定时器弹出定时器 20 秒跨度>
猜你喜欢
  • 2020-06-26
  • 1970-01-01
  • 1970-01-01
  • 2017-02-20
  • 2021-07-28
  • 2021-05-19
  • 1970-01-01
  • 2021-12-07
  • 2021-02-01
相关资源
最近更新 更多