如何在React中用requestAnimationFrame替代setTimeout和clearTimeout
用requestAnimationFrame替换setTimeout实现复制状态重置
首先修正原代码里的一个小问题:原点击事件回调里返回的清理函数不会被自动执行,因为这不是useEffect的清理逻辑。下面是用requestAnimationFrame替换后的完整实现,同时修复了内存泄漏和属性判断错误:
import { useState, useRef, useEffect } from 'react'; const MyComponent = () => { const [copyText, setCopyText] = useState("Copy"); const rafIdRef = useRef(null); const copyTextHandler = () => { setCopyText("Copied"); // 记录状态切换的开始时间 const startTime = Date.now(); const delayDuration = 3000; // 3秒延迟 const checkAndResetText = () => { const elapsed = Date.now() - startTime; if (elapsed >= delayDuration) { // 时间到,重置文本 setCopyText("Copy"); rafIdRef.current = null; } else { // 未到时间,继续请求下一帧检查 rafIdRef.current = requestAnimationFrame(checkAndResetText); } }; // 启动帧循环 rafIdRef.current = requestAnimationFrame(checkAndResetText); }; // 组件卸载时清理未完成的requestAnimationFrame useEffect(() => { return () => { if (rafIdRef.current) { cancelAnimationFrame(rafIdRef.current); } }; }, []); return ( <button onClick={copyTextHandler} disabled={copyText === "Copied"} > {copyText} </button> ); };
关键说明:
- 用
useRef存储requestAnimationFrame的ID,这样能在组件的整个生命周期内访问到它,方便后续取消 - 通过
Date.now()计算时间差来模拟3秒延迟,因为requestAnimationFrame本身是按浏览器渲染帧触发(通常60帧/秒),不是直接设置延迟时间 - 添加
useEffect的清理函数,确保组件卸载时如果状态重置还没完成,会取消掉帧请求,避免内存泄漏 - 修正了原代码中
disabled属性的错误判断:原代码误将事件处理函数和字符串比较,现在改为正确的状态值判断
requestAnimationFrame的优势是它的执行时机和浏览器渲染周期同步,不会在页面处于后台时频繁触发(部分浏览器会暂停后台标签的帧请求),相比setTimeout更适合和UI状态同步的场景。
内容的提问来源于stack exchange,提问作者Prachi Tripathi
相关产品推荐
相关产品推荐

