Reputation: 302
I have an application that requires an idle timeout that first warns the user that they will be logged out in one minute, then logs the user out after the one minute has expired.
I have had success using a class component as demonstrated in the following post:
Session timeout warning modal using react
I am moving the code for my application over to React Hooks but I'm having a hard time moving this code over. I have tried the following:
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');
};
In the end, I would like for a modal to appear to warn the user of impending logout. If the user moves the mouse, clicks, etc (see events) then the timer resets. If the user clicks on the button in the modal the timer is reset.
Thanks so much for the help!
Upvotes: 6
Views: 12891
Reputation: 2033
Based on the first answer, I have another hooks solution when you also need the time until the user gets logged out.
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;
Upvotes: 7
Reputation: 4839
I rewrote your answer a bit to get rid of liniting errors, I thought it would also be handy to extract it into a hook so then you just need to pass in the parameters you want, that eliminates the need to have the states for the timers.
The useCallback I suppose is for efficiency to stop it working itself out on each render,
I've moved some of the other stuff inside the UseEffect to get rid of those linting errors!
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
Upvotes: 2
Reputation: 594
Try this
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;
Upvotes: 11