【发布时间】:2019-06-01 06:23:19
【问题描述】:
问题总结:使用 React 的 useEffect 挂钩时,setTimeout 无法在移动设备上清除。但是,它们正在桌面上清除。
问题重现:https://codepen.io/amliving/pen/QzmPYE。
注意:在移动设备上运行以重现问题。
我的问题:为什么我的解决方案(如下所述)有效?
详情:
我正在创建一个自定义挂钩来检测空闲情况。我们称之为useDetectIdle。它从一组事件中动态添加和删除window 的事件侦听器,当触发事件时,在一段时间后通过setTimeout 调用提供的回调。
这里是动态添加到window然后从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)上,任何现有的超时在其调用组件卸载时都不会清除。 IE。传递给 setTimemout 的回调仍然会触发。
我的解决方案:经过反复试验,我发现从事件列表中删除 mousedown 可以解决问题。有谁知道幕后发生了什么?
【问题讨论】:
-
也许是 Chrome stackoverflow.com/questions/41181372/… 的故意更改,但我仍然很惊讶之前没有遇到这种情况
标签: javascript reactjs mouseevent settimeout react-hooks