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

React空投组件倒计时仅从20变为19后停止运行该如何修复?

问题根源

  • 闭包陷阱:countDown函数在首次渲染时被创建,捕获了初始状态下的timeState.seconds = 20和timer = 0,定时器每次执行时拿到的都是首次渲染的旧状态值,不会随组件更新获取最新的state,导致倒计时只走一次就停。
  • 直接在组件主体执行airdropReleaseTokens,每次组件重渲染都会触发该函数,可能重复创建定时器。
  • 旧版countDown逻辑中存在重复减秒的问题:先计算seconds = timeState.seconds -1,赋值时又写了seconds: seconds -1,相当于每次直接减2秒。
  • 清零判断逻辑使用的是闭包捕获的旧timeState.seconds值,永远无法触发清零逻辑,即使触发拿到的也是旧的timer值,无法正确清除定时器。
  • 缺少定时器清理逻辑,组件卸载时会造成内存泄漏。

修复后完整代码

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

const Airdrop = (props) => {
  const [timeState, setTimeState] = useState({ time: {}, seconds: 20 });
  // 用ref存储定时器id,规避闭包问题
  const timerRef = useRef(null);

  const secondsToTime = (secs) => {
    const hours = Math.floor(secs / (60 * 60));
    const divisor_for_minutes = secs % (60 * 60);
    const minutes = Math.floor(divisor_for_minutes / 60);
    const divisor_for_seconds = divisor_for_minutes % 60;
    const seconds = Math.ceil(divisor_for_seconds);
    return {
      h: hours,
      m: minutes,
      s: seconds,
    };
  };

  // 初始化时间显示
  useEffect(() => {
    setTimeState(prev => ({
      ...prev,
      time: secondsToTime(prev.seconds)
    }));
  }, []);

  // 监听质押余额,达标启动定时器
  useEffect(() => {
    const stakingThreshold = "50000000000000000000";
    if (props.stakingBalance >= stakingThreshold && !timerRef.current && timeState.seconds > 0) {
      timerRef.current = setInterval(countDown, 1000);
    }
    // 组件卸载/依赖变更时清理定时器
    return () => {
      if (timerRef.current) {
        clearInterval(timerRef.current);
        timerRef.current = null;
      }
    };
  }, [props.stakingBalance, timeState.seconds]);

  const countDown = () => {
    setTimeState(prevState => {
      const newSeconds = prevState.seconds - 1;
      // 秒数清零时清除定时器,可在此处添加空投发放逻辑
      if (newSeconds <= 0) {
        clearInterval(timerRef.current);
        timerRef.current = null;
        // 调用空投发放接口/方法
      }
      return {
        ...prevState,
        time: secondsToTime(newSeconds),
        seconds: newSeconds
      };
    });
  };

  return (
    <div style={{ color: "black" }}>
      {timeState.time.m}:{timeState.time.s}
    </div>
  );
};

export default Airdrop;

核心修改说明

  • 用useRef存储定时器ID,不受闭包影响,随时可以访问最新的定时器实例进行清除
  • 把启动定时器和清理逻辑都放到useEffect中,仅在质押余额满足条件且定时器未启动时才创建,避免重复创建
  • 所有状态修改都通过setTimeState回调中的prevState获取最新值,完全规避闭包旧值问题
  • 修正了减秒逻辑,每次仅减1秒,清零时直接在状态更新回调中清除定时器,可直接插入空投发放逻辑
  • 增加了组件卸载时的定时器清理逻辑,避免内存泄漏

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 17:45:01