React useEffect依赖属性变更时清除timeout 实现文档浏览页码延迟上报
问题根源
你当前的代码存在两个核心问题导致防抖逻辑失效:
useEffect回调直接使用async声明:async函数返回值是Promise对象,无法被React识别为清理函数,导致你写的clearTimeout逻辑完全没有正常执行,所有延迟上报任务都会触发- 定时器ID没有持久化存储:你把
timeout变量声明在useEffect回调内部,每次依赖项变化触发重执行时,拿到的都是全新的变量,清理函数根本获取不到上一次的定时器ID,自然也清不掉之前的上报任务
修复后的代码
使用useRef存储定时器ID,保证跨渲染周期可以访问到同一个定时器实例,同时去掉useEffect回调的async声明:
import { useRef, useEffect } from 'react'; // 组件内部逻辑 const timerRef = useRef(null); useEffect(() => { // 每次依赖变化先清空上一次的定时器 if (timerRef.current) { clearTimeout(timerRef.current); } if (scriptInfo && authData && numPages) { // 新定时器ID存入ref,跨渲染周期保留 timerRef.current = setTimeout(async () => { await postScriptAnalyticsData({ script_id: scriptInfo._id, user_id: authData.user.user_id, page: page, full_page: numPages }); }, 30000); // 30秒防抖时长 } // 组件卸载/依赖变化时清理定时器 return () => { if (timerRef.current) { clearTimeout(timerRef.current); } }; }, [scriptInfo, page, authData, numPages]);
可选优化:页面关闭前上报最后停留页码
如果用户在30秒防抖等待期内直接关闭页面,会导致最后一次停留的页码没有上报,可以追加页面卸载前的上报逻辑:
useEffect(() => { const handleBeforeUnload = () => { if (scriptInfo && authData && numPages) { // 直接上报当前页码,不需要等待 postScriptAnalyticsData({ script_id: scriptInfo._id, user_id: authData.user.user_id, page: page, full_page: numPages }); } }; window.addEventListener('beforeunload', handleBeforeUnload); return () => window.removeEventListener('beforeunload', handleBeforeUnload); }, [scriptInfo, authData, numPages, page]);
内容的提问来源于stack exchange,提问作者Michal Gally
相关产品推荐
相关产品推荐

