CodeZombie
CodeZombie

Reputation: 2087

Prompt user before the session ends

I need to display a dialog box where it shows the remaining time for the current session to expire. I have implemented it, unfortunately,the timer is ticking multiple times.Here is my code.

warningTime and timeout value is fetched from the api which is in parent component.

const [ remainingTime, setRemainingTime ] = useState(warningTime);

    useEffect(() => {
        let interval = null;
        if (timeout > 0) {
        let sessionTimeoutInterval = setInterval(() => {
            let runTime = localStorage.getItem("timeout");
            if (parseInt(runTime) === warningTime) {
                openDialog();
                if(remainingTime===warningTime) {
                    interval = setInterval(() => {
                        if (remainingTime > 0) {
                            setRemainingTime(remainingTime => remainingTime - 1);
                        }
                    }, 1000);
                }
                if(remainingTime === 0) {
                    handleDialogClose();
                    clearInterval(interval);
                }
            } else{
                localStorage.setItem("timeout", --runTime); 
            }
        }, 1000);
        if (remainingTime === 0) {
            handleDialogClose();
            handleLogout();
        }
        return () => {
            clearInterval(sessionTimeoutInterval);
        };
        }
    }, [timeout, remainingTime, warningTime ]);

remainingTime will be displayed in dialog.

Upvotes: 1

Views: 171

Answers (2)

Sohail Ashraf
Sohail Ashraf

Reputation: 10569

I have made couple of change in the code.

  1. I am using useRef to hold the status of the component. So in useEffect i am checking if the component is mounted and then setting the timer value from the localStorage or from the props value and on subsequent updates useEffect will not execute the code inside if (init.current) block.

useRef returns a mutable ref object whose .current property is initialized to the passed argument (initialValue). The returned object will persist for the full lifetime of the component

Note useRef doesn’t notify you when its content changes. Mutating the .current property doesn’t cause a re-render.

  1. I am using setTimeout to update the sessionTimeout state after every 1 second to update the timer, so the state update will execute the useEffect hook after each update as the sessionTimeout in included in the useEffect dependency.

Try this.

import React, { useEffect, useState, useRef } from "react";
import DialogBox from "./DialogBox";
import Logout from "./Logout";

export default function Child({ warningTime, timeout }) {
  const [showDialog, setShowDialog] = useState(false);
  const [showLogout, setShowLogout] = useState(false);
  const [sessionTimeout, setSessionTimeout] = useState(timeout);
  const init = useRef(true);
  const progressbar = useRef(warningTime);

  useEffect(() => {
    if (init.current) {
      init.current = false;
      let sessionTime = localStorage.getItem("timeout");

      if (sessionTime && sessionTime < warningTime) {
        progressbar.current = sessionTime;
      } else {
        progressbar.current = warningTime;
      }

      if (sessionTime) {
        setSessionTimeout(prevState => sessionTime);
      } else {
        localStorage.setItem("timeout", sessionTimeout);
      }
    }

    let sessionTimeoutInterval = null;
    if (sessionTimeout > 0) {
      sessionTimeoutInterval = setTimeout(() => {
        if (sessionTimeout <= warningTime) {
          openDialog();
        }
        setSessionTimeout(sessionTimeout => {
          let updatedtime = sessionTimeout - 1;
          localStorage.setItem("timeout", updatedtime);
          return updatedtime;
        });
      }, 1000);
    } else {
      localStorage.removeItem("timeout");
      handleDialogClose();
      handleLogout();
      clearTimeout(sessionTimeoutInterval);
    }
    return () => {
      if (sessionTimeoutInterval) clearTimeout(sessionTimeoutInterval);
    };
  }, [sessionTimeout]);

  function openDialog() {
    setShowDialog(true);
  }

  function handleDialogClose() {
    setShowDialog(false);
  }

  function handleLogout() {
    setShowLogout(true);
  }

  function addMoreTimeHandler() {
    handleDialogClose();
    setSessionTimeout(sessionTimeout => {
      localStorage.setItem("timeout", timeout);
      return timeout;
    });
  }

  return (
    <div>
      {showLogout ? <Logout /> : "Time remaning: " + sessionTimeout}
      {showDialog ? (
        <DialogBox
          progressBar={progressbar.current - 1}
          sessionTimeout={sessionTimeout}
          addMoreTime={addMoreTimeHandler}
        />
      ) : null}
    </div>
  );
}


Live Example

Edit Prompt user before the session ends

Upvotes: 1

Sanjay Soni
Sanjay Soni

Reputation: 227

I tried to simulate your code with dummy data and it worked.

let [ remainingTime, setRemainingTime, sessionTimeout, warningTime] = [1000, 5000, 10000, 1000];
let runTime = 3000;

   function abc() {
        let interval = null;
        if (sessionTimeout > 0) {
        let sessionTimeoutInterval = setInterval(() => {
            if (parseInt(runTime) === warningTime) {
                // openDialog();
console.log("open dialog");
                if(remainingTime===warningTime) {
                    interval = setInterval(() => {
                        if (remainingTime > 0) {
                            remainingTime -= 1000;
                        }
                    }, 1000);
                }
                if(remainingTime === 0) {
                    // handleDialogClose();
console.log("close dialog");
                    clearInterval(interval);
clearInterval(sessionTimeoutInterval);
                } 
            } else {
if(runTime > 0){ // this condition is newly added
                runTime-=1000; }
            }
        }, 1000);
        }
    }
abc();

Upvotes: 0

Related Questions