React/TypeScript中含状态依赖的setInterval停止运行问题
解决React中setInterval结合状态控制的问题
你的问题核心在于set_balance的依赖项导致定时器被意外清理,同时闭包捕获了旧的状态值。以下是正确的实现方案:
1. 用Ref同步最新状态
由于useCallback会捕获闭包内的旧状态值,直接依赖balance和check_balance会导致函数频繁重建,进而触发useEffect清理定时器。我们可以用useRef同步最新的状态值,避免将它们加入useCallback依赖:
const [balance, setBalance] = useState(0); const [check_balance, setCheckBalance] = useState(false); const intervalref = useRef(null); // 同步最新状态到ref const balanceRef = useRef(balance); const checkBalanceRef = useRef(check_balance); useEffect(() => { balanceRef.current = balance; }, [balance]); useEffect(() => { checkBalanceRef.current = check_balance; }, [check_balance]);
2. 优化set_balance回调函数
修改set_balance,使用ref获取最新状态,减少依赖项,同时添加错误处理避免异步操作中断定时器:
const set_balance = useCallback(async () => { try { if (!wallet?.publicKey) return; if (!checkBalanceRef.current) return; const current_balance = await get_current_balance(wallet); // 用ref中的最新余额做判断 if (current_balance !== balanceRef.current) { setCheckBalance(false); } setBalance(current_balance); } catch (err) { console.error("获取余额失败:", err); } }, [wallet, setBalance, setCheckBalance]);
3. 调整定时器的生命周期管理
修改useEffect逻辑,确保仅在wallet变化时替换定时器,其他情况保持定时器持续运行:
useEffect(() => { // 清理旧定时器 if (intervalref.current) { clearInterval(intervalref.current); intervalref.current = null; } // 当钱包存在时启动新定时器 if (wallet?.publicKey) { intervalref.current = setInterval(set_balance, 1000); } // 组件卸载时清理定时器 return () => { if (intervalref.current) { clearInterval(intervalref.current); intervalref.current = null; } }; }, [wallet, set_balance]);
逻辑说明
- 状态同步:通过
balanceRef和checkBalanceRef确保set_balance总能获取到最新的状态值,避免闭包陷阱。 - 稳定的回调:
set_balance仅依赖wallet和状态更新函数,不会因balance或check_balance变化而重建,减少useEffect的触发次数。 - 定时器持续运行:定时器只会在
wallet变化或组件卸载时被清理,其他情况下保持运行;当check_balance被设为false时,set_balance会提前退出,但定时器仍会继续触发,等待下一次setCheckBalance(true)的调用。
内容的提问来源于stack exchange,提问作者daoplays
相关产品推荐
相关产品推荐

