📘 useAwayTracker Hook 使用文档
useAwayTracker
是一个 React Hook,用于检测用户是否离开页面,并在回到页面时精确计算离开时长。
可应用于 自动刷新数据、安全登出、用户行为分析 等场景。
✨ 功能特性
-
核心功能
- 监听页面是否可见(
visibilitychange
) - 可选监听
blur/focus
(更细粒度检测) - 离开时记录时间、回到时计算离开秒数
- 监听页面是否可见(
-
API
isAway: boolean
— 当前是否离开awaySeconds: number
— 已离开秒数(实时更新)reset(): void
— 手动清零getCurrentAwaySeconds(): number
— 即时获取已离开秒数onReturn(seconds, reason)
— 回到页面时的回调
-
健壮性
- 不依赖后台的
setInterval
,避免限速误差 - 回到页面时通过时间差校准,保证数据准确
- 自动清理事件与定时器,避免内存泄漏
- 不依赖后台的
📦 Hook 源码
import { useState, useRef, useEffect, useCallback } from "react";
/**
* 页面离开/返回检测 Hook
* @param {(seconds: number, reason: string) => void} onReturn 回到页面时的回调
*/
export function useAwayTracker(onReturn) {
const [isAway, setIsAway] = useState(false);
const [awaySeconds, setAwaySeconds] = useState(0);
const awayStartRef = useRef(null);
const timerRef = useRef(null);
const getCurrentAwaySeconds = useCallback(() => {
if (!awayStartRef.current) return 0;
return Math.floor((Date.now() - awayStartRef.current) / 1000);
}, []);
const reset = useCallback(() => {
awayStartRef.current = null;
setAwaySeconds(0);
setIsAway(false);
}, []);
const markAway = useCallback((reason) => {
if (awayStartRef.current) return; // 已经在离开状态
awayStartRef.current = Date.now();
setIsAway(true);
// 启动一个计时器(仅用于前台展示,后台可能限速,但回来会修正)
timerRef.current = setInterval(() => {
setAwaySeconds(getCurrentAwaySeconds());
}, 1000);
console.debug("[useAwayTracker] markAway:", reason, new Date().toISOString());
}, [getCurrentAwaySeconds]);
const markReturn = useCallback((reason) => {
if (!awayStartRef.current) return;
const seconds = Math.floor((Date.now() - awayStartRef.current) / 1000);
clearInterval(timerRef.current);
timerRef.current = null;
awayStartRef.current = null;
setAwaySeconds(seconds);
setIsAway(false);
console.debug("[useAwayTracker] markReturn:", reason, "awaySeconds=", seconds);
if (onReturn) onReturn(seconds, reason || "return");
// 回来后 2 秒重置 awaySeconds 为 0(让 UI 回归)
setTimeout(() => setAwaySeconds(0), 2000);
}, [onReturn]);
useEffect(() => {
const handleVisibility = () => {
if (document.hidden) {
markAway("visibilitychange:hidden");
} else {
markReturn("visibilitychange:visible");
}
};
document.addEventListener("visibilitychange", handleVisibility, true);
window.addEventListener("blur", () => markAway("window:blur"), true);
window.addEventListener("focus", () => markReturn("window:focus"), true);
return () => {
document.removeEventListener("visibilitychange", handleVisibility, true);
window.removeEventListener("blur", () => markAway("window:blur"), true);
window.removeEventListener("focus", () => markReturn("window:focus"), true);
clearInterval(timerRef.current);
};
}, [markAway, markReturn]);
return {
isAway,
awaySeconds,
getCurrentAwaySeconds,
reset,
};
}
🚀 使用示例
import React from "react";
import { useAwayTracker } from "./useAwayTracker";
export default function Demo() {
const { isAway, awaySeconds } = useAwayTracker((seconds, reason) => {
console.log(`用户离开 ${seconds} 秒后回来(${reason})`);
if (seconds >= 10) {
alert("检测到你离开超过 10 秒,自动刷新数据!");
}
});
return (
<div>
<h3>H5 离开页面检测 Demo</h3>
{isAway
? `当前离开中... 已离开 ${awaySeconds} 秒`
: "当前页面活跃中 ✅"}
</div>
);
}
⚡ 行为说明
-
离开页面
- 页面
hidden
或window.blur
→isAway = true
awaySeconds
开始计数- 页面标题可选修改为
【离开中】xxx
- 页面
-
回到页面
- 页面
visible
或window.focus
→isAway = false
- 自动计算离开时长 → 回调
onReturn(seconds, reason)
- UI 2 秒后重置计时为 0
- 页面
🛠 应用场景
-
数据刷新 回到页面后自动刷新数据,保证实时性。
-
安全策略 长时间离开后要求重新登录。
-
用户体验 离开时暂停动画 / 视频,回到时恢复播放。