Home > Back-end >  setTimeout and clearTimeout in React
setTimeout and clearTimeout in React

Time:03-17

I'm struggling with creating a logout feature when users don't make any click event for 30 seconds on the page using setTimeout and clearTimeout.

But every time the users click anything on the page, the remaining time must be reset to 30 seconds again( or as an another option, clearTimeOut and setTimeOut will be used.) Meanwhile when the users don't click anything on the page, they are gonna be logged out automatically after 30seconds by removing the accessToken.

So to resolve the problem, my approach is like this:

  • setTimeout() will be begun when the users come over to this page.

  • when the users click something on the page, clearTimeOut will be active and setTimeOut will be active again too

  • when the users don't click anything on the page for 30 seconds, they are gonna be logged out automatically by removing the accessToken in the local storage

Currently making the users logged out after 30seconds by removing the accessToken works! and setTimeOut in UseEffect works too.

The only matter is I have no idea how to make clearTimeOut() and setTimeOut work when the users make click event on the page..

import styled from 'styled-components';
import React, {useRef, useEffect, useState} from 'react';
import ScreenContainer from '../../src/component/common/ScreenContainer';
import {useNavigate as useDomNavigate} from 'react-router-dom';
import isNil from 'lodash/isNil';
import userClient from '../../src/client/user/userClient';

const Device = () => {
    const domNavigate = useDomNavigate();
    const [storeId, setStoreId] = useState(() => JSON.parse(localStorage.getItem('storeId')));
    const [currentDeposit, setCurrentDeposit] = useState<number>(0);
    const depositBalanceInfo = userClient.getDepositBalanceByStoreId(storeId, isNil(storeId)).data;

    const [time, setTime] = useState(1500);

    const logout = () => {
        localStorage.removeItem('accessToken');
        domNavigate(`/home/home`);
    };

    //////////////////////////
    // below is the code that I wrote to make it work..
 

    const myFunc = () => {
        // remove accessToken for logout
        localStorage.removeItem('accessToken');

        // move to home page
        domNavigate(`/home/home`);
    }

    // begin setTimeOut automatically when the users come over to this page from another one.
    useEffect(() => {
        const timeoutBegins = window.setTimeout(myFunc, 3000);
        return () => clearTimeout(timeoutBegins); 
    }, [])

    // when the users click anything on the page, it clears current setTimeOut function and restart the setTimeOut function again.
    const clickDisplay = () => {
        clearTimeout(timeoutBegins);
        timeOutBegins();
    }



   ///////////////////////////////////////// 

    useEffect(() => {
        if (storeId && depositBalanceInfo) {
            setCurrentDeposit(depositBalanceInfo?.depositBalance);
        }
    }, [storeId, depositBalanceInfo?.depositBalance]);

    return (
        <ScreenContainer>
            <Wrapper>
                <div>Choose Payment Option</div>
                <button onClick={() => window.history.back()}>go back</button>
                <div>Your Balance: {currentDeposit.toLocaleString()}dollar</div>
                <br />
                <button onClick={() => domNavigate(`/charge/step2-select-price/?chargeMethod=card`)}>Credit Card Payment</button>
                <br />
                <button onClick={() => domNavigate(`/charge/step2-select-price/?chargeMethod=cash`)}>Cash Payment</button>
                <br />
                <button onClick={() => domNavigate(`/home/checkUserByPin`)}>Reset Password</button>
                <br />
                <button onClick={logout}>Logout</button>
            </Wrapper>
        </ScreenContainer>
    );
};

const Wrapper = styled.div`
    border: 1px solid red;
`;

export default Device;

CodePudding user response:

There's a couple of issues that jump out from your snippet:

  1. timeoutBegins is scoped to your useEffect callback so isn't available to clickDisplay.
  2. clickDisplay is not attached to any event listeners
  3. timeoutBegins is not callable, it is the timer ID

Note: it's a good idea to create a minimal reproducable example as this will help both you and reviewers eliminate the problem.

Solution:

let timerId;

function Device() {
  const logOutUser = () => {
   // log out code...
  };

  const startTimer = () => {
    if (timerId) clearTimeout(timerId); 
    timerId = setTimeout(logOutUser, 3000);
  }

  const stopTimer = () => clearTimeout(timerId);

  useEffect(() => {
    // start timer when component is mounted
    startTimer();

    // stop timer when component is unmounted
    return stopTimer;
  }, []);


  return <div onClick={startTimer}></div>;
}

CodePudding user response:

You could do something like this. So, you run the timer and every time you clicking somewhere in the document it will cancel the current timer and run the new. So your myFunc function will run only if user doesn't click on the page after N seconds.

Keep in mind, you want to do it in 30 sec you will need to put 30000 in setTimeout

const timerId = useRef(null)

  const myFunc = () => {
    clearTimeout(timerId.current)
    timerId.current = null
    console.log('DO SOMETHING')
  }

  const onDocumentClick = () => {
    if (timerId.current) {
      clearTimeout(timerId.current)
      timerId.current = window.setTimeout(myFunc, 3000)
    }
  }

  useEffect(() => {
    timerId.current = window.setTimeout(myFunc, 3000)

    document.addEventListener('click', onDocumentClick)
    return () => {
      clearTimeout(timerId.current)
      document.removeEventListener('click', onDocumentClick)
    }
  }, [])
  • Related