首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用钩子响应组件同步状态计数与本地存储的时间

使用钩子响应组件同步状态计数与本地存储的时间
EN

Stack Overflow用户
提问于 2021-06-25 16:37:15
回答 1查看 877关注 0票数 1
代码语言:javascript
复制
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中实现倒计时,它将与本地存储同步,并通过浏览器选项卡与显示计数器的页面同步,希望同步状态定时器和本地存储定时器,将它们放在定时器的useEffect中,更改状态更新定时器,并在setTimeout中更新本地存储。帮帮我,谢谢

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-06-25 20:08:17

您的代码有一些问题。

第一个问题是setTimeout接受回调函数,这是它的第一个agrument。它将在作为第二个参数提供的时间之后执行。但是,不是将函数countTimer作为回调传递,而是传递它的执行结果。

代码语言:javascript
复制
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变量发生变化时,您都会从localStorage读取timer键并再次设置setTimer。当state发生变化时,useEffect会引起对安排另一个渲染的反应。而您的代码将触发几乎无止境的循环。这将使组件不断更新,直到时间耗尽为止。

还有另一个问题。如果您的组件在超时仍然运行时被卸载。挂起的函数最终将被执行。它将向localStorage写入卸载前的timer值。如果您再次挂载此组件,则执行挂起超时时可能会覆盖新创建的值。

这是一个很好的习惯,以清除任何悬而未决的超时或其他影响的卸载。所以如果setTimeout还在运行的话,你最好还是清理它。为此,您必须将cleanup函数作为useEffect钩子的返回值返回。

总结如下:

  • 将回调函数传递给setTimeout,而不是其执行的结果
  • 不要创建多个挂起超时
  • 不要在useEffect中触发无休止的重发器
  • 清除组件卸载上挂起的超时

总之,应该是这样的:

代码语言:javascript
复制
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

码箱链接

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/68134405

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档