React挂钩功能组件的空闲超时

时间:2019-08-07 16:03:39

标签: javascript reactjs react-hooks

我有一个需要空闲超时的应用程序,该应用程序首先警告用户他们将在一分钟后注销,然后在一分钟后将用户注销。

我在使用类组件方面取得了成功,如以下博文所示:

Session timeout warning modal using react

我正在将应用程序代码移到React Hooks上,但是我很难移开该代码。我尝试了以下方法:

const [signoutTime, setSignoutTime] = useState(0);
let warnTimeout;
let logoutTimeout;

const setTimeout = () => {
  warnTimeout = setTimeout(warn, warningTime);
  logoutTimeout = setTimeout(logout, signoutTime);
};

const clearTimeout = () => {
  if (warnTimeout) clearTimeout(warnTimeout);
  if (logoutTimeout) clearTimeout(logoutTimeout);
};

useEffect(() => {
  setWarningTime(10000);
  setSignoutTime(15000);
  const events = [
    'load',
    'mousemove',
    'mousedown',
    'click',
    'scroll',
    'keypress'
  ];

  const resetTimeout = () => {
    clearTimeout();
    setTimeout();
  };

  for (var i in events) {
    window.addEventListener(events[i], resetTimeout);
  }

  setTimeout();
});

const warn = () => {
  console.log('Warning');
};

const destroy = () => {
  console.log('Session destroyed');
};

最后,我希望出现一个模式来警告用户即将退出。如果用户移动鼠标,单击等(见事件),则计时器重置。如果用户单击模式中的按钮,计时器将重置。

非常感谢您的帮助!

3 个答案:

答案 0 :(得分:4)

尝试

mport React, { useEffect, useState } from 'react';
    const LogoutPopup = () => {
        const [signoutTime, setSignoutTime] = useState(10000);
        const [warningTime, setWarningTime] = useState(15000);
        let warnTimeout;
        let logoutTimeout;

        const warn = () => {
            console.log('Warning');
        };
        const logout = () => {
            console.log('You have been loged out');
        }

        const destroy = () => {
            console.log('Session destroyed');
        }
        const setTimeouts = () => {
            warnTimeout = setTimeout(warn, warningTime);
            logoutTimeout = setTimeout(logout, signoutTime);
        };

        const clearTimeouts = () => {
            if (warnTimeout) clearTimeout(warnTimeout);
            if (logoutTimeout) clearTimeout(logoutTimeout);
        };

        useEffect(() => {

            const events = [
                'load',
                'mousemove',
                'mousedown',
                'click',
                'scroll',
                'keypress'
            ];

            const resetTimeout = () => {
                clearTimeouts();
                setTimeouts();
            };

            for (let i in events) {
                window.addEventListener(events[i], resetTimeout);
            }

            setTimeouts();
            return () => {
                for(let i in events){
                    window.removeEventListener(events[i], resetTimeout);
                    clearTimeouts();
                }
            }
        },[]);


        return <div></div>
    }
    export default LogoutPopup;

答案 1 :(得分:2)

我稍微重写了您的答案以消除初始化错误,我认为将其提取到钩子中也很方便,因此您只需要传递所需的参数即可,而无需具有状态计时器。

我认为useCallback是为了提高效率,以使其停止在每个渲染器上正常工作

我已经在UseEffect中移动了其他一些东西,以消除这些掉毛错误!

import React, { useCallback } from 'react'


function useMonitor({ timeout, warningTimeout, onWarn, onTimeOut }) {

  const warn = useCallback(()=> {
    onWarn && onWarn()    
  },[onWarn]) 

  const logout = useCallback(()=> {
    onTimeOut && onTimeOut()
  },[onTimeOut]) 

  React.useEffect(() => {
    let warnTimeout;
    let logoutTimeout;

    const setTimeouts = () => {
      warnTimeout = setTimeout(warn, warningTimeout);
      logoutTimeout = setTimeout(logout, timeout);
    };

    const clearTimeouts = () => {
      if (warnTimeout) clearTimeout(warnTimeout);
      if (logoutTimeout) clearTimeout(logoutTimeout);
    };
    const events = [
      'load',
      'mousemove',
      'mousedown',
      'click',
      'scroll',
      'keypress'
    ];

    const resetTimeout = () => {
      clearTimeouts();
      setTimeouts();
    };

    for (let i in events) {
      window.addEventListener(events[i], resetTimeout);
    }

    setTimeouts();
    return () => {
      for (let i in events) {
        window.removeEventListener(events[i], resetTimeout);
        clearTimeouts();
      }
    }
  }, [logout, timeout, warn, warningTimeout]);

}

export default useMonitor

答案 2 :(得分:2)

基于第一个答案,当您还需要用户注销之前的时间时,我还有另一个钩子解决方案。

Codesandbox Demo

useAutoLogout.js

import React, { useEffect, useState } from "react";
const useLogout = (startTime) => {
  const [timer, setTimer] = useState(startTime);
  useEffect(() => {
    const myInterval = setInterval(() => {
      if (timer > 0) {
        setTimer(timer - 1);
      }
    }, 1000);
    const resetTimeout = () => {
      setTimer(startTime);
    };
    const events = [
      "load",
      "mousemove",
      "mousedown",
      "click",
      "scroll",
      "keypress"
    ];
    for (let i in events) {
      window.addEventListener(events[i], resetTimeout);
    }
    return () => {
      clearInterval(myInterval);
      for (let i in events) {
        window.removeEventListener(events[i], resetTimeout);
      }
    };
  });
  return timer;
};

export default useLogout;

App.js

import useAutoLogout from "./useAutoLogout";
function App() {
  const timer = useAutoLogout(10);

  if (timer == 0) {
    return <div>Logged Out</div>;
  }

  if (timer < 8) {
    return <div>In {timer} seconds you will be automatically logged out</div>;
  }
  return <div>Signed in</div>;
}

export default App;