apply react setInterval, clearInterval useEffect - javascript

I am trying to give a timer every 1 second in JSX using moment .
const onlive = useOnlive(); // react-query Custom Hook
console.log(onlive);
{
"event": {
"id": 933,
"startDate": "2023-01-17T01:00:00.000Z"
},
"nextEvent": {
"id": 930,
"startDate": "2023-01-18T13:20:00.000Z"
}
}
I tried to create a timer by calculating nextEvent - currentTime and using the setInteval and clearInterval functions in useEffect to calculate the remaining time,
Timer doesn't work.
Why doesn't the timer (setInteval, clearInterval) fire every second?
Does the timer work when the useOnlive Custom API Hook is called every second?
const nextStartTime = moment(onlive.data?.nextEvent?.startDate); // 2023-01-18
const nowTime = moment(); // current time
useEffect(() => {
const nextStartTimeId = setInterval(() => {
setHour(nextStartTime.diff(nowTime, 'hours', false) % 24);
setMinute(nextStartTime.diff(nowTime, 'minutes', false) % 60);
setSecond(nextStartTime.diff(nowTime, 'seconds', false) % 60);
}, 100);
return () => clearInterval(nextStartTimeId);
}, [setHour, setMinute, setSecond, nextStartTime, nowTime]);
return (
<span>
The session starts {hour < 10 ? "0" + hour : hour}:
{minute < 10 ? "0" + minute : minute}:
{second < 10 ? "0" + second : second} later.
</span>
)
How can I make the timer work?

You're setting the interval to 100 ms instead of 1000 ms
const nextStartTimeId = setInterval(() => {
setHour(nextStartTime.diff(nowTime, "hours", false) % 24);
setMinute(nextStartTime.diff(nowTime, "minutes", false) % 60);
setSecond(nextStartTime.diff(nowTime, "seconds", false) % 60);
}, 1000);

Related

How to make Timer countdown to 00:00, stay on this value for a while and change to another cycle?

Now I’m working on my version of 25 + 5 Clock, this is my version on codepen: https://codepen.io/faber_g/pen/WNJOOwp
And this is example from Freecodecamp and tasks to complete: https://codepen.io/freeCodeCamp/full/XpKrrW (example)
https://www.freecodecamp.org/learn/front-end-development-libraries/front-end-development-libraries-projects/build-a-25--5-clock (tasks)
The main idea of this timer is when timer countdown to "00:00" it should switch between "Break" and "Session" cycles. I do it in this way:
<div id="time-left" style={this.state.timerAmount < 60 ? { color: 'red' } : { color: 'black' }}>{this.clockInfo() === "00:00" ? this.changeName() : this.clockInfo()}</div>
The problem of my code is that it doesn't countdown to "00:00" or it switchs too fast, so you can only see "00:01" and then it changes to new cycle.
First in my function this.changeName() I go with this code, but it swithes too fast, so you can not see "00:00":
changeName() {
if (this.state.timerAmount === 0 && this.state.timerName === 'Session') {
this.playSound();
setTimeout(() => {this.setState(state => ({
timerAmount: this.state.breakMinutes * 60,
timerName: 'Break'
})); },2000);
}
else if (this.state.timerAmount === 0 && this.state.timerName === 'Break') {
this.playSound();
this.setState(state => ({
timerAmount: this.state.sessionMinutes * 60,
timerName: 'Session'
})); }
}
Then I've tried by using innerHTML/innerText/innerContent first stay on "00:00" and after that using setTimeout for this.setState to change velue for new cycle with delay, but in this case I get empty element for some time and then it is start new cycle:
changeName() {
if (this.state.timerName === 'Session') {
document.getElementById('time-left').innerHTML = "00:00"
this.playSound();
setTimeout(() => {this.setState(state => ({
timerAmount: this.state.breakMinutes * 60,
timerName: 'Break'
})); },2000);
}
else if (this.state.timerName === 'Break') {
document.getElementById('time-left').innerText = "00:00";
this.playSound();
setTimeout(() => {this.setState(state => ({
timerAmount: this.state.sessionMinutes * 60,
timerName: 'Session'
}));} , 2000);
}
How to make Timer countdown to 00:00, stay on this value for a while and change to another cycle?
I found my problem.
In clockInfo() I have condition if (timer > 0) return "00:00". So when my timer showed "00:00", it start changeName() function and that is how I got empty element for some time.
I deleted changeName() and isertes it's conditions into clockInfo() like this:
clockInfo() {
if (this.state.timerAmount < 0) {
if (this.state.timerName === 'Session') {
"00:00"
this.playSound();
this.setState(state => ({
timerAmount: this.state.breakMinutes * 60,
timerName: 'Break'
}));
} else if (this.state.timerName === 'Break') {
"00:00"
this.playSound();
this.setState(state => ({
timerAmount: this.state.sessionMinutes * 60,
timerName: 'Session'
}));
}
} else {
let minutes = Math.floor(this.state.timerAmount / 60);
let seconds = this.state.timerAmount - minutes * 60;
seconds = seconds < 10 ? '0' + seconds : seconds;
minutes = minutes < 10 ? '0' + minutes : minutes;
return minutes + ':' + seconds;
}
}
When timer < 0, first it shaw "00:00", then play sound and then start function, but in this case without any empty element. So now all works very well.

How to modify count down timer in javascript code

is there a way to manipulate a count-down timer to change the time of the countdown to zero much faster? here is the code for the:
function CalcTimePercent(i, lastpayment, nextpayment, t, p) {
var time = nextpayment - t;
var hour = parseInt(time / 3600);
if ( hour < 1 ) hour = 0;
time = parseInt(time - hour * 3600);
if ( hour < 10 ) hour = '0'+hour;
var minutes = parseInt(time / 60);
if ( minutes < 1 ) minutes = 0;
time = parseInt(time - minutes * 60);
if ( minutes < 10 ) minutes = '0'+minutes;
var seconds = time;
if ( seconds < 10 ) seconds = '0'+seconds;
timer = hour+':'+minutes+':'+seconds;
document.getElementById('deptimer'+i).innerHTML = timer;
if(timer == "00:00:00") {
top.location.href='';
}
if(timer == "00:00:0-64") {
top.location.href='';
}
t = t + 1;
setTimeout("CalcTimePercent("+i+", "+lastpayment+", "+nextpayment+", "+t+", "+p+")",1000);
}
The easiest way I can think of is using JavaScript's Date class. You would get the time the timer would end, as well as the current time, and constantly check the difference between the two. Here is how you would do that in your case:
let timer = null;
function runTimer(time) {
clearInterval(timer);
timer = setInterval(() => {
const timer = new Date(time.getTime() - new Date().getTime()); // Get difference between now and the end time
const timerText = `${timer.getUTCHours()}h ${timer.getMinutes()}m ${timer.getSeconds()}s`; // Generate a stylised version of the text
// document.getElementById("ELEMENT_ID").textContent = timerText; // Apply the stylised text to the timer element
console.log(timerText); // Apply the stylised text to the timer element
}, 1000); // Run this every 1000ms (1 second)
}
runTimer(new Date(Date.now() + 10 * 60000)); // Start timer for 10 minutes in the future
setTimeout(() => { // Wait 5 seconds
runTimer(new Date(Date.now() + 15 * 60000)); // Update timer for 15 minutes in the future
}, 5000);
The limitation of this method include the fact that it only supports times up to 24 hours. Any time over that will loop back (A timer of 27 hours will show up as "2h 0m 0s").

JavaScript Countdown timer with returned value after every interval

I am trying to write a countdown timer that returns the current countdown value from the function. So far I have managed to write the countdown function. It counts down but the function returns undefined. How can I modify the code to return the countdown value?
const startTimer = (duration, onlySeconds) => {
var timer = duration,
minutes,
seconds;
const interval = setInterval(function () {
minutes = parseInt(timer / 60, 10);
seconds = parseInt(timer % 60, 10);
minutes = minutes < 10 ? "0" + minutes : minutes;
seconds = seconds < 10 ? "0" + seconds : seconds;
if (timer-- === 0) {
clearInterval(interval)
}
console.log('seconds', seconds)
return !onlySeconds ? minutes + ":" + seconds : seconds;
}, 1000);
};
console.log('You will be logged out in ', startTimer(3, true))
You can't return from the asynchronous setInterval callback and expect to access that returned value. Instead, I might recommend passing a logger function to which you can pass the amount of time remaining. In that function, you can do whatever you want with that time. Here's a version that just does some console logging.
const startTimer = (duration, onlySeconds, logger) => {
var timer = duration,
minutes,
seconds;
const interval = setInterval(function () {
minutes = parseInt(timer / 60, 10);
seconds = parseInt(timer % 60, 10);
minutes = minutes < 10 ? "0" + minutes : minutes;
seconds = seconds < 10 ? "0" + seconds : seconds;
if (timer-- === 0) {
clearInterval(interval)
}
const time = !onlySeconds ? minutes + ":" + seconds : seconds;
logger(time);
}, 1000);
};
const logger = (time) => {
console.log('You will be logged out in ' + time);
}
startTimer(300, false, logger)
it is much simplier and direct to the point
const startTimer = (duration, onlySeconds) => {
var timer = duration,
minutes,
seconds;
const interval = setInterval(function () {
minutes = parseInt(timer / 60, 10);
seconds = parseInt(timer % 60, 10);
minutes = minutes < 10 ? "0" + minutes : minutes;
seconds = seconds < 10 ? "0" + seconds : seconds;
if (timer-- === 0) {
clearInterval(interval)
}
console.log('You will be logged out in '+ (!onlySeconds ? minutes + ":" + seconds : seconds));
}, 1000);
};
startTimer(3, true);
I think a different strategy might be in order.
class Countdown {
constructor() {
this._init();
}
_init(restart=false) {
this.target = undefined;
this.running = false;
this.terminate = false;
if(!restart) {
this.ms = undefined;
this.tick = [];
this.done = [];
}
}
// Set a callback that is called every second. `fn(remaining)`
ontick(fn) {
this.tick.push(fn);
}
// Set a callback that is called when the countdown ends. `fn()`
ondone(fn) {
this.done.push(fn);
}
// Start countdown with duration in milliseconds
start(ms) {
const now = performance.now();
if(this.running) throw new Exception('Countdown already running.');
this.running = true;
this.ms = ms;
this.target = now + ms;
this.update = () => {
const now = performance.now();
if(this.terminate) {
this._init();
return;
}
const remaining = this.target - now;
if(remaining > 100) {
setTimeout(this.update, remaining % 1000);
this.tick.forEach(fn => fn(remaining));
}
else {
this.done.forEach(fn => fn());
this._init();
}
};
setTimeout(this.update, this.target % 1000);
}
// Restart a running countdown. Optionally provide a new duration.
restart(ms=this.ms) {
const now = performance.now();
if(!this.running) throw new Exception('Countdown not running.');
this.ms = ms;
this.target = now + ms;
}
// Stop a running countdown.
stop() {
if(!this.running) throw new Exception('Countdown not running.');
this.terminate = true;
}
}
Then you would use it like this:
const countdown = new Countdown();
countdown.ontick(remaining => {
console.log('Remaining:', remaining, 'ms');
});
countdown.ondone(() => {
console.log('Countdown completed.');
});
countdown.start(15*60*1000); // 15 minutes
If you want to restart a running countdown:
countdown.restart(5*60*1000); // Update target duration to 5 minutes and restart
countdown.restart(); // Use previous duration and restart
If you want to cancel the countdown:
countdown.stop();

Cant Make a function run again

I made a timer that will reach zero.
and when it reaches zero make the timer run again.
the timer goes back to the starting number but doesnt run again.
also when i call it again the numbers just start to jump.
the code:
var timerPlace = document.getElementById('timer');
var timerP = document.getElementById('timerHard');
var stopTimer;
var toStop;
function timeMed() {
console.log('im in!')
var counter = 0;
var timeLeft = 5;
timerPlace.innerHTML = '00:45';
function timeIt() {
console.log('here')
counter++
timerPlace.innerHTML = convertSeconds(timeLeft - counter);
if (timerPlace.innerHTML == '00:00') {
clearInterval(stopTimer);
resetExercise();
timeMed();
}
}
function convertSeconds(s) {
var sec = s % 60;
var min = Math.floor((s % 3600) / 60);
return ('0' + min).slice(-2) + ':' + ('0' + sec).slice(-2);
}
if (!stopTimer) {
stopTimer = setInterval(timeIt, 1000);
}
}
You only call setInterval() when stopTimer is not set. But after the countdown completes, stopTimer is still set to the ID of the old interval timer, so you don't restart it. You should clear the variable when you call clearInterval().
if (timerPlace.innerHTML == '00:00') {
clearInterval(stopTimer);
stopTimer = null;
resetExercise();
timeMed();
}
Modern ES6 Approach and best practices.
I've decided to take the chance and refactor your code a little with Javascripts best practices in mind.
I've added comments which explain the code, and the engineering considerations.
The baseline for the timer is taken from the excellent answer here: https://stackoverflow.com/a/20618517/1194694
// Using destructuring on the paramters, so that the keys of our configuration object,
// will be available as separate parameters (avoiding something like options.duraitons and so on.
function startTimer({duration, onUpdate , infinite}) {
let timer = duration, minutes, seconds;
let interval = setInterval(function () {
minutes = parseInt(timer / 60);
seconds = parseInt(timer % 60);
// you can also add hours, days, weeks ewtc with similar logic
seconds = seconds < 10 ? `0${seconds}` : seconds;
minutes = minutes < 10 ? `0${minutes}` : minutes;
// calling your onUpdate function, passed from configuraiton with out data
onUpdate({minutes, seconds});
if (--timer < 0) {
// if infinite is true - reset the timer
if(infinite) {
timer = duration;
} else {
// Clearing the interval + additonal logic if you want
// I would also advocate implementing an onEnd function,
// So that you'll be able to decide what to do from configuraiton.
clearInterval(interval);
}
}
}, 1000);
}
const duration = 5;
const displayElement = document.querySelector("#timer");
startTimer({
duration,
onUpdate: ({minutes, seconds}) => {
// now you're not constraint to rendering it in an element,
// but can also Pass on the data, to let's say your analytics platform, or whatnot
displayElement.textContent = `${minutes}:${seconds}`;
},
infinite: true
});
<div id="timer">
</div>

How to format time with AngularJS filters?

I have a timer that is counting down from a value that is being set by some user.
They enter some number which is being treated as minutes. number * 1000 * 60 to convert it into milliseconds - and from there the counter is counting down and decrements this number.
What I want to do is format this number as minutes and seconds so if the entered number is 3.5, it should be shown as 03:30.
I've tried using the date filter which is provided by Angular but it's not in sync with the value of the timer. It would decrement once and then stops.
Code:
<h4 class="timer">{{ vm.timer.ticks | date: "mm:ss"}}</h4>
The background for the timer is in a service
start(duration) {
this.ticks = duration;
this.timer = this.$interval(() => {
if (this.ticks === 0) return this.stop();
this.ticks--;
}, 1000);
}
I think it is because you are hiding "this" in your inner function. Try to rewrite it as (untested!):
start(duration) {
var ticks = duration;
this.timer = this.$interval(() => {
if (ticks === 0) return this.stop();
ticks--;
}, 1000);
}
Ok, so... I made a terrible mistake when I was calculating the time...
I set up $scope.$watch to watch over my variable from the service and according to that i was changing couple other variables.
$scope.$watch('vm.timer.ticks', value => {
if (value <= 0) {
vm.timer.count = 0;
vm.timer.latest = '';
}
if(vm.timer.ticks > 0) {
seconds = vm.timer.ticks % 60;
minutes = Math.floor(vm.timer.ticks / 60);
filledMinutes = minutes < 10 ? "0" + minutes : minutes;
filledSeconds = seconds < 10 ? "0" + seconds : seconds;
vm.displayTime = filledMinutes + ':' + filledSeconds;
} else {
vm.displayTime = '00:00';
}
});

Categories