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.
- 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 theAdmin
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;
- Add
clearInterval
to yourstopCountdown
function. You can remove the clearInterval in thetimeIt
function and move it intostopCountdown
. 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 }