You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React定时器组件useState未完全更新:控制台与DOM显示不一致

React定时器组件:控制台打印state始终为0的问题解决

问题核心原因

你遇到的是React的闭包陷阱:当startConting函数执行并创建定时器时,回调函数捕获的是该函数执行时刻的second、minute、hour值(也就是初始的0)。之后组件每次因state更新重新渲染时,新的state值不会被这个已经存在的定时器回调捕获,导致控制台一直打印0,同时时间进位逻辑也因为依赖旧的state值完全失效。

另外你的时间进位逻辑本身也存在错误:比如判断秒数是否该进位时用了second > 60,但实际秒数到60就该进位;分钟的判断同理,小时的逻辑也完全颠倒了。

解决步骤

  1. 用useRef保存最新state值:Ref的.current属性会实时更新,能让定时器回调获取到最新的state。
  2. 修正时间进位逻辑:调整判断条件,确保秒到60进1分,分到60进1时。
  3. 清理定时器:用useRef保存定时器ID,避免重复点击Start创建多个定时器,同时在组件卸载时清除定时器。

修正后的代码

import { useState, useRef, useEffect } from 'react';

export const Timer = () => {
  const [hour, setHour] = useState(0);
  const [minute, setMinute] = useState(0);
  const [second, setSecond] = useState(0);
  
  // 用ref保存最新的state值和定时器ID
  const timeRef = useRef({ hour, minute, second });
  const intervalRef = useRef(null);

  // 同步state到ref,确保ref始终持有最新值
  useEffect(() => {
    timeRef.current = { hour, minute, second };
  }, [hour, minute, second]);

  const startCounting = () => {
    // 先清除之前的定时器,避免多个定时器同时运行
    if (intervalRef.current) {
      clearInterval(intervalRef.current);
    }

    intervalRef.current = setInterval(() => {
      const { hour: currentHour, minute: currentMinute, second: currentSecond } = timeRef.current;
      
      let newSecond = currentSecond + 1;
      let newMinute = currentMinute;
      let newHour = currentHour;

      // 处理秒进位
      if (newSecond >= 60) {
        newSecond = 0;
        newMinute += 1;
        
        // 处理分进位
        if (newMinute >= 60) {
          newMinute = 0;
          newHour += 1;
          
          // 这里可以根据需求处理小时上限,比如到24归0
          if (newHour >= 24) {
            newHour = 0;
          }
        }
      }

      setHour(newHour);
      setMinute(newMinute);
      setSecond(newSecond);
      
      // 现在打印的是最新值
      console.log(newSecond);
    }, 1000);
  };

  // 组件卸载时清除定时器
  useEffect(() => {
    return () => {
      if (intervalRef.current) {
        clearInterval(intervalRef.current);
      }
    };
  }, []);

  return (
    <>
      <div className="conter">
        <span className="hours time">
          {hour >= 10 ? hour : `0${hour}`}
        </span>
        <span className="time"> : </span>
        <span className="minutes time">
          {minute >= 10 ? minute : `0${minute}`}
        </span>
        <span className="time"> : </span>
        <span className="seconds time">
          {second >= 10 ? second : `0${second}`}
        </span>
        <button onClick={startCounting}>
          Start
        </button>
      </div>
    </>
  );
};

关键说明

  • timeRef用来同步最新的state值,定时器回调通过它获取当前时间,避免闭包陷阱。
  • intervalRef保存定时器ID,确保重复点击Start时不会创建多个定时器,同时组件卸载时能清理定时器。
  • 重新梳理了时间进位逻辑,确保秒、分、时的进位规则正确。

内容的提问来源于stack exchange,提问作者APIPAWE KATOTO Daniel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 02:20:32