问题摘要:使用React的setTimeout
挂钩时,useEffect
在移动设备上未清除。但是,它们正在桌面上清除。
问题重现:https://codepen.io/amliving/pen/QzmPYE。
NB:在移动设备上运行以重现该问题。
我的问题:为什么我的解决方案(如下所述)有效?
详细信息:
我正在创建一个自定义钩子以检测空闲状态。我们称之为useDetectIdle
。它会动态地从一组事件中向window
添加事件监听器,或从中删除事件侦听器,这些事件监听器在触发一段时间后会通过setTimeout
调用提供的回调。
这是将动态添加到window
然后从其中删除的事件的列表:
const EVENTS = [
"scroll",
"keydown",
"keypress",
"touchstart",
"touchmove",
"mousedown", /* removing 'mousedown' for mobile devices solves the problem */
];
这是useDetectIdle
钩子。此处的重要之处在于,此钩子在其调用组件卸载时应清除所有现有的超时(并删除所有事件侦听器):
const useDetectIdle = (inactivityTimeout, onIdle) => {
const timeoutRef = useRef(null);
const callbackRef = useRef(onIdle);
useEffect(() => {
callbackRef.current = onIdle;
});
const reset = () => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
const id = setTimeout(callbackRef.current, inactivityTimeout);
timeoutRef.current = id;
};
useEffect(() => {
reset();
const handleEvent = _.throttle(() => {
reset();
}, 1000);
EVENTS.forEach(event => window.addEventListener(event, handleEvent));
return () => {
EVENTS.forEach(event => window.removeEventListener(event, handleEvent));
timeoutRef.current && clearTimeout(timeoutRef.current);
};
}, []);
};
useDetectIdle
在这样的内部组件中被调用:
const Example = () => {
useDetectIdle(5000, () => alert("first"));
return <div className="first">FIRST</div>;
};
在非触摸屏设备上,useDetectIdle
可以完美运行。但是在移动设备(iOS和Android)上,卸载其调用组件时,不会清除任何现有的超时 。即传递给setTimemout
的回调仍然会触发。
我的解决方案:通过一些反复试验,我发现从事件列表中删除mousedown
可以解决问题。有人知道引擎盖下发生了什么吗?