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

React中setTimeout未重置问题:自定义useTimer Hook修复方案

修复useTimer Hook的计时器重置问题

问题背景

点击按钮后会显示Hello文本,5秒后文本消失。但第一次点击按钮3秒后再次点击时,Hello文本会在2秒后就消失——前一次触发的计时器没有被重置,导致旧的回调依然会执行,覆盖了新的计时逻辑。我们需要修改useTimer Hook,实现多次点击或调用时自动重置计时器,保证最后一次操作的计时生效。

原代码

import "./styles.css";
import React, { useRef, useCallback, useState, useEffect } from "react";
const useTimer = () => {
  const timer = useRef();
  const fn = useCallback((callback, timeout = 0) => {
    timer.current = setTimeout(() => {
      callback();
    }, timeout);
  }, []);

  const clearTimeoutHandler = () => {
    console.log("clear");
    return clearTimeout(timer);
  };

  useEffect(() => {
    return clearTimeoutHandler();
  }, []);

  return { fn, clearTimeoutHandler };
};

export default function App() {
  const [state, setState] = useState(false);
  const timer = useTimer();
  const onClickHandler = () => {
    setState(true);
    timer.fn(() => {
      console.log(5000);
      setState(false);
    }, 5000);
  };
  return (
    <div className="App">
      {state && <h1>Hello</h1>}
      <button onClick={onClickHandler}>Open</button>
    </div>
  );
}

修复方案

核心修改点是每次调用计时函数前先清除旧计时器,同时修正清除计时器的参数错误:

import "./styles.css";
import React, { useRef, useCallback, useState, useEffect } from "react";
const useTimer = () => {
  const timer = useRef();
  
  const fn = useCallback((callback, timeout = 0) => {
    // 先清除之前的计时器,实现重置
    if (timer.current) {
      clearTimeout(timer.current);
    }
    // 创建新的计时器
    timer.current = setTimeout(() => {
      callback();
    }, timeout);
  }, []);

  const clearTimeoutHandler = () => {
    console.log("clear");
    // 修正:传入timer.current而非ref对象本身
    if (timer.current) {
      clearTimeout(timer.current);
      timer.current = null;
    }
  };

  useEffect(() => {
    return clearTimeoutHandler();
  }, []);

  return { fn, clearTimeoutHandler };
};

export default function App() {
  const [state, setState] = useState(false);
  const timer = useTimer();
  const onClickHandler = () => {
    setState(true);
    timer.fn(() => {
      console.log(5000);
      setState(false);
    }, 5000);
  };
  return (
    <div className="App">
      {state && <h1>Hello</h1>}
      <button onClick={onClickHandler}>Open</button>
    </div>
  );
}

修改说明

  1. 重置计时器逻辑:在fn函数中,每次创建新计时器前,先检查并清除timer.current中保存的旧计时器,确保只有最后一次的计时会生效。
  2. 修正清除函数参数:clearTimeout需要传入的是setTimeout返回的计时器ID,也就是timer.current,而非ref对象本身,之前的写法是无效的。
  3. 清理时重置ref:在clearTimeoutHandler中清除计时器后,将timer.current设为null,避免内存泄漏。

这样修改后,无论用户点击多少次按钮,都会重置计时器,Hello文本会在最后一次点击后显示满5秒才消失。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 04:26:03