You need to enable JavaScript to run this app. React.js用setInterval实现带暂停/恢复的计数器,渲染数值不更新
优惠活动
大模型
产品
解决方案
定价
更多

React.js用setInterval实现带暂停/恢复的计数器,渲染数值不更新

代码核心问题定位
  • 非响应式变量失效:代码中通过var声明的elapsed、secondsLeft属于组件渲染作用域内的临时变量,每次组件重新渲染都会被重置为初始值20;同时修改这类普通变量不会触发React重渲染,即便计数器逻辑正常运行,页面上的数值也不会同步更新。
  • 计时器重复创建:直接在组件函数体中调用timer()的写法,会在每一次组件渲染时都新建一个setInterval,多个计时器叠加运行,不仅逻辑混乱还会造成内存泄漏。
  • 闭包值过期:setInterval的回调会捕获创建时的作用域变量,拿到的isPlay、secondsLeft永远是首次创建时的初始值,后续状态更新后,回调内也读取不到最新值。
  • 暂停逻辑反转:当前代码中isPlay初始为false(默认应为运行状态),但间隔回调内写的是if (isPlay === true)就暂停计数,逻辑完全颠倒。
修正后可直接运行的实现
import { useState, useEffect, useRef } from 'react';

const ProgressBar = (props) => {
  // 剩余秒数通过state存储,修改后自动触发重渲染
  const [secondsLeft, setSecondsLeft] = useState(
    props.duration ? props.duration : 20
  );
  // 运行状态标识:true为已暂停,false为运行中
  const [isPaused, setIsPaused] = useState(false);
  // 用ref存储计时器实例,跨渲染保持同一引用
  const intervalRef = useRef(null);

  // 统一处理计时器的创建、清除逻辑
  useEffect(() => {
    // 暂停状态下直接清除已有计时器
    if (isPaused) {
      clearInterval(intervalRef.current);
      return;
    }

    intervalRef.current = setInterval(() => {
      setSecondsLeft(prev => {
        // 倒计时到0时清除计时器
        if (prev <= 0) {
          clearInterval(intervalRef.current);
          return 0;
        }
        // 基于上一次的state值递减,避免闭包捕获旧值
        return prev - 1;
      })
    }, 1000);

    // 组件卸载时清除计时器,避免内存泄漏
    return () => clearInterval(intervalRef.current);
  }, [isPaused, secondsLeft <= 0]);

  // 切换暂停/恢复状态
  const toggleStatus = () => {
    setIsPaused(prev => !prev);
  };

  return (
    <>
      <p>剩余时间:{secondsLeft} 秒</p>
      <button onClick={toggleStatus}>
        {isPaused ? '恢复' : '暂停'}
      </button>
    </>
  );
};

export default ProgressBar;
实现逻辑说明
  • 倒计时数值存储在useState中,每一次递减都会触发React重渲染,保证页面展示的数字和实际计数逻辑完全同步
  • 计时器实例存储在useRef中,不会因为组件重渲染丢失引用,暂停时直接清除对应实例即可中断计数,恢复时基于当前剩余秒数重新创建计时器,实现断点续跑
  • 所有副作用逻辑统一收敛在useEffect中,根据运行状态动态调整计时器生命周期,从根源上避免闭包捕获旧状态的问题

内容的提问来源于stack exchange,提问作者Thomas James

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.24 19:45:01