setTimeout无法通过移动设备上的React useEffect挂钩清除

时间:2019-01-06 08:46:45

标签: javascript reactjs mouseevent settimeout react-hooks

问题摘要:使用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可以解决问题。有人知道引擎盖下发生了什么吗?

0 个答案:

没有答案
相关问题