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> ); }
修改说明
- 重置计时器逻辑:在
fn函数中,每次创建新计时器前,先检查并清除timer.current中保存的旧计时器,确保只有最后一次的计时会生效。 - 修正清除函数参数:
clearTimeout需要传入的是setTimeout返回的计时器ID,也就是timer.current,而非ref对象本身,之前的写法是无效的。 - 清理时重置ref:在
clearTimeoutHandler中清除计时器后,将timer.current设为null,避免内存泄漏。
这样修改后,无论用户点击多少次按钮,都会重置计时器,Hello文本会在最后一次点击后显示满5秒才消失。
内容的提问来源于stack exchange,提问作者Asking
相关产品推荐
相关产品推荐

