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

React严格模式下自定义useCountDown钩子异常排查

React严格模式下useCountDown钩子异常问题排查与修复

问题原因

React严格模式在开发环境中会双重执行组件初始化、useEffect回调、状态更新的函数式回调等逻辑,导致你的倒计时钩子中被重复创建多个定时器,多个countdown函数同时触发状态更新,最终表现为倒计时无间隔跳至0且无法停止。

核心问题点(常见错误)

  1. 未在启动新倒计时前清理旧定时器,导致多个定时器并行触发
  2. 使用状态变量跟踪计时状态,严格模式下状态更新的重复执行会导致逻辑混乱
  3. 未在组件卸载时清理定时器,存在内存泄漏风险

修复方案

通过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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 00:54:56