React 中的会话超时与模态

Session timeout in React with modal

我使用 react-idle-timer 创建了一个会话超时,它在第一次时运行良好,但是当我在模态警告中单击“继续会话”时,它不会将超时重置为原始设置(本例中为 30 秒)。单击继续会话后,它会每 5 秒打开一次警告模式。

SessionTimeout.js

import React, { useState, useRef } from "react";
import IdleTimer from "react-idle-timer";
import { useDispatch } from "react-redux";
import { bindActionCreators } from "redux";
import { actionCreators } from '../state/index';
import { useHistory } from "react-router-dom";
import { useSelector } from "react-redux";
import SessionTimeoutDialog from './SessionTimeoutDialog';

let countdownInterval;
let timeout = 1000 * 30 * 1;

const SessionTimeout = () => {
    const idleTimer = useRef(null);
    const history = useHistory();

    const login = useSelector((state) => state.login);  // this is just to see if a user is logged
    const dispatch = useDispatch();
    const { logout } = bindActionCreators(actionCreators, dispatch); // this is to logout

    const [timeoutModalOpen, setTimeoutModalOpen] = useState(false);
    const [timeoutCountdown, setTimeoutCountdown] = useState(0);

    const onActive = () => {
        // timer reset automatically.
        if (!timeoutModalOpen) {
            clearSessionInterval();
            clearSessionTimeout();
        }
    };
    
    const onIdle = () => {
        const delay = 1000 * 5;
        if(login.logged && !timeoutModalOpen) {
            timeout = setTimeout(() => {
                let countDown = 60;
                setTimeoutModalOpen(true);
                setTimeoutCountdown(countDown);
                countdownInterval = setInterval(() => {
                  if (countDown > 0) {
                    setTimeoutCountdown(--countDown);
                  } else {
                    handleLogout();
                  }
                }, 1000);
            }, delay);
        }
    };

    const clearSessionTimeout = () => {
        clearTimeout(timeout);
    };
    
    const clearSessionInterval = () => {
        clearInterval(countdownInterval);
    };

    const handleLogout = () => {
        setTimeoutModalOpen(false);
        clearSessionInterval();
        clearSessionTimeout();
        logout();
        history.push("/login");
    };
      
    const handleContinue = () => {
        setTimeoutModalOpen(false);
        clearSessionInterval();
        clearSessionTimeout();
    };

    return (
        <>
          <IdleTimer
            ref={idleTimer}
            onActive={onActive}
            onIdle={onIdle}
            debounce={250}
            timeout={timeout}
          />
          <SessionTimeoutDialog
            countdown={timeoutCountdown}
            onContinue={handleContinue}
            onLogout={handleLogout}
            open={timeoutModalOpen}
          />
        </>
    );
};

export default SessionTimeout;

这是我的模态(对话框)。 SessionTimeoutDialog.jsx

import React from "react";
import * as PropTypes from "prop-types";
import {
  Dialog,
  DialogTitle,
  DialogContent,
  DialogActions,
  Button,
  Typography,
  makeStyles,
  Slide
} from "@material-ui/core";
import clsx from "clsx";
import red from "@material-ui/core/colors/red";

const useStyles = makeStyles(() => ({
    dialog: {
      borderRadius: 0
    },
    button: {
      borderRadius: 0,
      textTransform: "none",
      padding: 5
    },
    logout: {
      color: "#fff",
      backgroundColor: red[500],
      "&amp;:hover": {
        backgroundColor: red[700]
      }
    },
    countdown: {
      color: red[700]
    }
}));

const Transition = React.forwardRef(function Transition(props,ref) {
    return <Slide direction="up" ref={ref} {...props} />;
});

export default function SessionTimeoutDialog({
    open,
    countdown,
    onLogout,
    onContinue
}) {
    const classes = useStyles();
    return (
        <Dialog
            open={open}
            aria-labelledby="session-timeout-dialog"
            aria-describedby="session-timeout-dialog"
            classes={{ paper: classes.dialog }}
            TransitionComponent={Transition}
        >
            <DialogTitle id="session-timeout-dialog-title">
                Session Timeout
            </DialogTitle>
            <DialogContent>
                <Typography variant="body2">
                    The current session is about to expire in{" "}
                    <span className={classes.countdown}>{countdown}</span> seconds.
                </Typography>
                <Typography variant="body2">{`Would you like to continue the session?`}</Typography>
            </DialogContent>
            <DialogActions>
                <Button
                    onClick={onLogout}
                    variant="contained"
                    className={clsx(classes.logout, classes.button)}
                >
                Logout
                </Button>
                <Button
                    onClick={onContinue}
                    color="primary"
                    variant="contained"
                    className={classes.button}
                >
                    Continue Session
                </Button>
            </DialogActions>
        </Dialog>
    );
}

SessionTimeoutDialog.propTypes = {
    /**
     * indicator whether the dialog is open/close
     */
    open: PropTypes.bool.isRequired,
    /**
     * the countdown timer.
     */
    countdown: PropTypes.number.isRequired,
    /**
     * callback function to handle closing action
     */
    onLogout: PropTypes.func.isRequired,
    /**
     * callback function to handle confirm action.
     */
    onContinue: PropTypes.func.isRequired
};

这是一个沙箱示例:https://codesandbox.io/s/festive-dust-dzt1t

单击“继续会话”后如何将超时设置回默认的 30 秒或我想要的任何值?

谢谢

您可以使用 useBnIdle 自定义挂钩进行用户空闲检测。您还可以使用 startTimer 函数

重新启动计时器
npm i use-bn-idle

您可以使用startTimer函数来启动用户空闲检测,它接受秒作为参数。 useBnIdle 需要一个箭头函数作为参数,如果用户在指定时间内空闲,该函数将执行。您可以在该函数中编写您的逻辑来显示模态。

下面的例子有重启按钮,点击它我们可以再次触发 startTimer 秒。它将重新启动用户空闲检测。

import {useBnIdle} from 'use-bn-idle';

const App = () => {
  const [startTimer, stopTimer] = useBnIdle(() => {
    //Will execute if the user is idle for a certain period of time.
    //You can write logic like showing popup or log out the user.
    console.log('Expired: Session Timeout');
  })
  
  useEffect(() => {
    // start the user idle detection
    startTimer(60); //passed 60 seconds as argument.
  })

  const restartTimer = () => {
     startTimer(30)
  }

  return <div>
    <button onClick={stopTimer}>Stop Detection</button>
    <button onClick={restartTimer}>Restart Timer</button>
  </div>
}