Skip to content
Advertisement

React Countdown Timer unable to make a stop function

I am practically new to React. In this App I am using Hooks! I’ve made a Countdown Timer that will show in a few seconds after logging in. I am unable to make it stop on a button click. I need some advise on this as I’ve been struggling for the past 2 days with this. This is my code so far: (Please Help)

function Admin() {
    const [isTimerOpen, setTimmer] = useState(false);
    let history = useHistory();

    // SET BY THE ADMIN
    var minutesToCountDown = 0.9;
    // TRANSFORM INTO SECONDS
    var transformMinutesToSeconds = minutesToCountDown * 60
    // KEEP A STATE
    const [counterValue, setCounterValue] = useState(0);
    const [isTimmerStoped, setStopTimer] = useState(false);

    // FUNCTION TO HAPPEN EVERY 1 SECOND
    function timeIt() {
        if (isTimmerStoped === false) {
            transformMinutesToSeconds--
            setCounterValue(transformMinutesToSeconds)
            console.log("Timer is on: ", transformMinutesToSeconds)

            if (transformMinutesToSeconds === 0) {
                clearInterval(interval)
                setStopTimer(true)
            }
        } else {
            setStopTimer(true)
            clearInterval(interval)
        }
    }

    // STARTS THE COUNTDOWN
    var interval;
    const startCountdown = () => {
        interval = setInterval(timeIt, 1000)
    }

    const stopCountdown = () => {
        console.log("Stop Timer")
        setStopTimer(true);
        setCounterValue(0);
        setTimmer(false);
    }

    // ADD 0 IN FRONT ON THE TIME REMAINING
    const addLeadingZeros = value => {
        value = String(value);
        while (value.length < 2) {
            value = `0${value}`;
        }
        return value;
    };

    // CONVERT SECONDS INTO TIME REMAINING
    function convertSeconds(seconds) {
        var min = Math.floor(seconds / 60);
        var sec = seconds % 60;
        return addLeadingZeros(min) + ':' + addLeadingZeros(sec)
    }

    const logOutUser = () => {
        logout();
        return history.push(mainRoute)
    }

    function setTimer() {
        const timer = setTimeout(() => {
            setTimmer(true)
            console.log('This will run after 3 seconds!')
            startCountdown()

        }, sessionTimeout);
        return () => clearTimeout(timer);
    }

    useEffect(() => {
        if (isTimmerStoped === false) {
            console.log('Effect Starting', isTimmerStoped)
            setTimer()
        } else {
            console.log('Effect Stopping', isTimmerStoped)
            stopCountdown()
        }

      }, [isTimmerStoped, setStopTimer, minutesToCountDown]);



    return <React.Fragment>
            <CssBaseline />
            <Container disableGutters maxWidth={false}>
                <NavigationBar handleSignOut={logOutUser}/>
                <TimerContent 
                    timeRemaining={convertSeconds(counterValue)}
                    isTimerAlertOpen={isTimerOpen}
                    extendSessionBtn={stopCountdown}
                    logoutBtn={logOutUser}  
                    clickOutsideButton={stopCountdown}/>
            </Container>
    </React.Fragment>  
}

export default Admin;

Advertisement

Answer

You should do 2 things.

  1. Make the interval variable a ref. This way it’s value will be unique every where it imported. Note: Just creating a variable above the component is a bad idea because that variable will be shared between each component that imports the Admin component, which will lead to bugs.

Wrong

let interval;

function Admin() {
  //... code here

  // STARTS THE COUNTDOWN
  // var interval; Remove from here
  const startCountdown = () => {
      interval = setInterval(timeIt, 1000)
  }

  //... code here
}

export default Admin;

Right

function Admin() {
  const interval = React.useRef(); 
  //... code here

  // STARTS THE COUNTDOWN
  // var interval; Remove from here
  const startCountdown = () => {
      interval.current = setInterval(timeIt, 1000)
  }

  //... code here
}

export default Admin;
  1. Add clearInterval to your stopCountdown function. You can remove the clearInterval in the timeIt function and move it into stopCountdown. Please take a look at this codepen I made to demostrate
const stopCountdown = () => {
  console.log("Stop Timer")
  setStopTimer(true);
  setCounterValue(0);
  setTimmer(false);
  clearInterval(interval.current) // Clear the interval here
}
User contributions licensed under: CC BY-SA
7 People found this is helpful
Advertisement