I’m currently trying to make a countdown timer that actively updates the paragraph element with the ‘#otp-timer’ id. However, I am unsure how to display the amount of seconds left with each second passing by. I am only able to display the updated message after 5 seconds and even then it just happens without me clicking the button.
I’m aware I’m nowhere near finished and just want to focus on this feature at the moment.
You can think of effect as something that can run without any user action (maybe a bit like event listener). Using button to trigger effect can be done, but not directly - that’s not how effects work - using useEffect as on-click callback will only call the useEffect function, not the effect which was earlier defined.
However, pressing the button could change something that would trigger the effect.
Still a little confused here. I wouldn’t be able to pass in anything else as a function for the onClick event handler or rather prop as thats what its referred to. For instance, countDownTimer is out of scope. Unless you’re referring to something not currently present in the program within it’s current state.
Other functions which are in the scope, can be used as callbacks for click, but yes, in current code there isn’t defined any that would do something useful.
Notice that when setCount(count - 1) is not commented, each change of the count will right now trigger another change of the count. This is how effect with dependency works - every change to the dependency will run the effect. Therefore this ends up a bit like infinite recursion.
Ah, I get it now. So, I tried to create another state variable that represents the seconds being ticked down. The count state variable, which is used as a dependency, is no longer being altered within the useState hook. I added some comments in my code to help with clarity.
The current issue is that the timer state variable is not being rendered every passing second or possibly even changing every passing second within the handleTimer() function. It should tick down from 5 to 0 then the state variable’s value will be reset back to 5 in case the user clicks the button again. That way it ticks down from 5 rather than going into the negatives.
Code:
const { useState, useEffect, useRef } = React;
export const OTPGenerator = () => {
const [count, setCounter] = useState(0);
const [timer, setTimer] = useState(5);
const [timerMsg, setTimerMsg] = useState('');
useEffect(() => {
// setTimer(timer - 1);
const countDownTimer = setTimeout(() => {
setTimerMsg("OTP expired. Click the button to generate a new OTP.");
// Once expiration message is displayed, reset the state variable's value back to 5
setTimer(timer + 5);
}, 5000);
return () => clearTimeout(countDownTimer);
}, [count]);
function handleTimer() {
setTimerMsg(`Expires in: ${timer} seconds`);
// Update count since its a dependency and a change will cause the useEffect() hook to run again
setCounter(count + 1);
// Update the timer state variable every second to decrement it from 5 to 0 before displaying expiration message.
setInterval(() => setTimer(timer - 1), 1000);
}
return (
<div className="container">
<h1 id="otp-title">OTP Generator</h1>
<h2 id="otp-display">Click 'Generate OTP' to get a code</h2>
<p id="otp-timer" aria-live=''>{timerMsg}</p>
<button onClick={handleTimer} id="generate-otp-button">Generate OTP</button>
</div>
);
};