React严格模式下自定义useCountDown钩子异常排查
React严格模式下useCountDown钩子异常问题排查与修复
问题原因
React严格模式在开发环境中会双重执行组件初始化、useEffect回调、状态更新的函数式回调等逻辑,导致你的倒计时钩子中被重复创建多个定时器,多个countdown函数同时触发状态更新,最终表现为倒计时无间隔跳至0且无法停止。
核心问题点(常见错误)
- 未在启动新倒计时前清理旧定时器,导致多个定时器并行触发
- 使用状态变量跟踪计时状态,严格模式下状态更新的重复执行会导致逻辑混乱
- 未在组件卸载时清理定时器,存在内存泄漏风险
修复方案
通过useRef存储定时器ID和计时状态(useRef的值不会触发重渲染,且不受严格模式重复执行的影响),同时完善定时器清理逻辑:
修复后的代码示例
import { useState, useRef, useEffect } from 'react'; export const useCountDown = (initialSeconds: number) => { const [count, setCount] = useState(initialSeconds); const [timing, setTiming] = useState(false); // 用ref存储定时器ID,避免状态更新导致的重复创建 const timerRef = useRef<NodeJS.Timeout | null>(null); // 用ref跟踪当前是否在计时,不受严格模式重复执行影响 const isActiveRef = useRef(false); // 组件卸载时强制清理定时器 useEffect(() => { return () => { if (timerRef.current) clearTimeout(timerRef.current); }; }, []); const countdown = () => { setCount(prev => { if (prev <= 0) { setTiming(false); isActiveRef.current = false; return 0; } return prev - 1; }); // 仅当计时状态激活时,才设置下一个定时器 if (isActiveRef.current) { timerRef.current = setTimeout(countdown, 1000); } }; const start = () => { // 防止重复启动倒计时 if (isActiveRef.current) return; // 重置计数与状态 setCount(initialSeconds); setTiming(true); isActiveRef.current = true; // 清理可能存在的旧定时器 if (timerRef.current) clearTimeout(timerRef.current); countdown(); }; return { count, timing, start }; };
关键修复细节
useRef存储定时器与状态:避免严格模式下状态更新导致的逻辑重复执行,确保计时状态的准确性- 前置清理定时器:启动新倒计时前强制清除旧定时器,避免多定时器并行
- 组件卸载清理:通过
useEffect的清理函数,防止组件卸载后定时器仍在运行导致内存泄漏 - 重复启动拦截:通过
isActiveRef判断当前是否在计时,避免重复触发start逻辑
内容的提问来源于stack exchange,提问作者Junlin
相关产品推荐
相关产品推荐

