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
相关产品推荐
相关产品推荐

