React clearInterval not working in class Component - javascript

I am trying to make a timer that will start at the beginning of the sorting visualization and run until sorting is over. The timer starts accordingly but does not stop after sorting. this works absolutely fine in functional Component (with useEffect hook) but does not work in class Component.
here is my startTimer function -
startTimer = () => {
let isAlgo = this.state.isAlgorithmSortOver;
let interval;
if (isAlgo === true) {
interval = setInterval(() => {
this.setState({
time: this.state.time + 10,
});
}, 10);
} else if (isAlgo === false) {
clearInterval(interval);
}
// return () => clearInterval(interval);
};
and here is startVisualizer function -
startVisualizer = () => {
let steps = this.state.arraySteps;
let colorSteps = this.state.colorSteps;
this.clearTimeouts();
let timeouts = [];
let i = 0;
while (i < steps.length - this.state.currentStep) {
let timeout = setTimeout(() => {
let currentStep = this.state.currentStep;
this.setState({
array: steps[currentStep],
colorElement: colorSteps[currentStep],
currentStep: currentStep + 1,
isAlgorithmSortOver: false,
});
//? comparing the currentStep with arraySteps and the state of isAlgorithmSortOver will remain false until the array is fully sorted.. Adding '+ 1' to currentStep because the arraySteps state always will be '+1' bigger than the currentStep..
if (currentStep + 1 === i) {
this.setState({
isAlgorithmSortOver: true,
});
}
timeouts.push(timeout);
}, this.state.delayAnimation * i);
i++;
}
this.startTimer();
this.setState({
timeouts: timeouts,
// isAlgorithmSortOver: false,
});
};

because you are not clearing the interval. Try to save interval's id to state like this:
startTimer = () => {
let isAlgo = this.state.isAlgorithmSortOver;
if (isAlgo === true) {
interval = setInterval(() => {
this.setState({
time: this.state.time + 10,
});
}, 10);
this.setState({interval})
}
};
Then you can then call clearInterval wherever you want, like this:
clearInterval(this.state.interval)
You also should bring out timeouts.push(timeout) from setTimeout where in while. Because, otherwise timeouts.push(timeout) does not work synchronously, it works after a while.

Related

How to create countup timer with keyboard events in js

I am creating a program where the timer starts when i hit "keyup" and stops when i hit "keydown" and resets the timer when i hit "keydown" next time.
const Timer = () => {
const [timer, setTimer] = useState(0);
let state = 0;
let isFired = false;
const increment = useRef(null);
useEffect(() => {
window.addEventListener('keydown', (e) => {
if (isFired) {
if (e.code === 'Space' && state === 2) {
e.stopPropagation();
isFired = false;
handleReset();
}
if (e.code === 'Space' && state === 1) {
clearInterval(increment.current);
state = 2;
}
}
});
window.addEventListener('keyup', (e) => {
if (!isFired) {
if (e.code === 'Space' && state === 0) {
isFired = true;
state = 1;
handleStart();
}
}
});
return () => {
window.removeEventListener('keydown', handleReset);
window.removeEventListener('keyup', handleStart);
};
}, []);
const handleStart = () => {
increment.current = setInterval(() => {
// DON'T COPY THIS BIT
setTimer((timer) => timer + 10);
}, 10);
};
const handleReset = () => {
clearInterval(increment.current);
setTimer(0);
state = 0;
};
// DON'T COPY THIS BIT
const formatTime = () => {
console.log(timer);
const getMilliSeconds = `0${timer % 60}`.slice(-2);
const seconds = `${Math.floor(timer / 60)}`;
const getSeconds = `0${seconds % 60}`.slice(-2);
const getMinutes = `0${Math.floor(timer / 3600)}`.slice(-2);
// return `${getHours} : ${getMinutes} : ${getSeconds}`;
if (getMinutes === '00') {
return `${getSeconds}.${getMilliSeconds}`;
} else {
return `${getMinutes} : ${getSeconds} : ${getMilliSeconds} `;
}
};
// const formatTime = () => {
// // const milliSeconds = `0${}`;
// const seconds = `0${Math.floor(timer / 100)}`;
// const minute = `0${Math.floor(timer / 3600)}`.slice(-2);
// return `${minute}.${seconds}`;
// };
return (
<div className="Timer">
<h1>{formatTime()}</h1>
</div>
);
};
i have tried this so far
it works most of the time but sometimes it gets glitchy
and i know the time is not formated properly and also the increment is also wrong. Sometimes it stops on the "keydown" and fires the "keyup" in the same stroke making it reset the timer starting from zero I don't exactly know that if it fires keyup on the same stroke but it seems like it does
this is a link to what i have done so far timer
I checked your code and found it needs too many changes.
You are using state and isFired as variables only but you need their persisted values on rerendering. Therefore, these must be states.
You are not unbinding the same listener which you binded on keyup and keydown.
As per your current code, your timer will start on 1st keyup and then next keydown it will stop as such where it was. Now, on next keydown it will reset to 0 and then on keyup, again timer will start from 0.
I modified your code and see working changes here,
https://codesandbox.io/s/xenodochial-shannon-im8zt?file=/src/App.js

VueJS how to watch a value and animate when it changes

In my vuejs-application I want to animate a number/value when it changes. The value is based on a response from the vuex-store.
so I've got the animation running, but for some reason the animation loop is infinite and just continues all the time.
data() {
return {
interval: false,
hitsCount: 0
}
},
watch: {
hitsCount: function() {
clearInterval(this.interval);
this.interval = window.setInterval( function() {
let change = this.hitsCount / 10;
change = change >= 0 ? Math.ceil(change) : Math.floor(change);
this.hitsCount = this.hitsCount + change;
}.bind(this),20);
}
}
the template is simply:
<div> {{hitsCount}} </div>
The value hitsCount comes from a request:
this.$store.dispatch("store/data", {}).then(response => {
this.hitsCount = response.total;
.../
});
the value changes each time there is a new request.
As mentioned before, the animation starts right away and keeps counting endlessly - what am I missing here?
Anyway, it's not good idea to watch property you are changing.
Divide it into two variables.
data() {
return {
hitsCount: 0,
hitsCountDisplay: 0
}
},
watch: {
hitsCount() {
clearInterval(this.interval);
if (this.hitsCount !== this.hitsCountDisplay) {
let value = this.hitsCountDisplay;
const change = (this.hitsCount - this.hitsCountDisplay) / 30;
this.interval = setInterval(() => {
value += change;
this.hitsCountDisplay = Math.round(value);
if (this.hitsCountDisplay === this.hitsCount) {
clearInterval(this.interval);
}
}, 20);
}
}
}
and
<div> {{hitsCountDisplay}} </div>
Here is a working example: https://jsfiddle.net/knvyrx8j/
You shouldn't update a property inside its watcher, this will cause an infinite loop, but you could run the animation inside the callback of your action dispatcher :
this.$store.dispatch("store/data", {}).then(response => {
let count = response.total;
clearInterval(this.interval);
this.interval = window.setInterval( function() {
let change = count / 10;
change = change >= 0 ? Math.ceil(change) : Math.floor(change);
this.hitsCount = this.hitsCount + change;
}.bind(this),20);
});

React - get time of long pressing button in seconds

I'm trying to return the number of seconds whilst holding in a button.
eg: "click+ hold, inits -> counts & displays 1, 2, 3, 4, 5 -> leaves button -> resets back to 0"
I've gotten close. It works fine, in my console, but whenever I try to update the state it ends up in an infinite loop.
import React, { useState, useEffect } from "react";
const Emergency = () => {
let counter = 0;
let timerinterval;
const [ms, setMs] = useState(counter);
const timer = start => {
console.log("tick tock");
console.log(start);
if (start === true && counter >= 1) {
timerinterval = setInterval(() => {
counter += 1;
console.log(counter);
setMs(counter); //When I remove this, the infinite loop disappears.
}, [1000]);
} else {
setMs(0);
}
};
const pressingDown = e => {
console.log("start");
e.preventDefault();
counter = 1;
timer(true);
};
const notPressingDown = e => {
console.log("stop");
e.preventDefault();
timer(false);
setMs(0);
clearInterval(timerinterval);
};
return (
<>
<button
onMouseDown={pressingDown}
onMouseUp={notPressingDown}
onTouchStart={pressingDown}
onTouchEnd={notPressingDown}
className="button is-primary mt-3"
>
Emergency
</button>
<br />
Time holding it is.... {ms}
</>
);
};
export default Emergency;
An easy way would be to calculate the time difference between mouseDown and mouseUp, but for the sake of UX, I would like to {ms} to update live as I'm holding the button.
Any suggestions?
Thanks!
There are two problems with your code:
You are not clearing interval. timeInterval is a new variable whenever your component is re-rendered. You need to use ref (const timeInterval = React.useRef(null); ... timeInterval.current = ... ; clearInterval(timeInterval.current);
Also you need to remove counter = 1; from your pressingDowm function, because before each setMs you are incrementing it by one
const Emergency = () => {
let counter = 0;
let timerinterval = React.useRef((null as unknown) as any);
const [ms, setMs] = React.useState(counter);
const timer = (start: any) => {
console.log('tick tock');
console.log(start);
if (start === true && counter >= 1) {
timerinterval.current = setInterval(() => {
console.log(counter);
setMs(counter); //When I remove this, the infinite loop disappears.
counter += 1;
//#ts-ignore
}, [1000]);
} else {
setMs(0);
}
};
const pressingDown = (e: any) => {
console.log('start');
e.preventDefault();
counter = 1;
timer(true);
};
const notPressingDown = (e: any) => {
console.log('stop');
e.preventDefault();
timer(false);
setMs(0);
clearInterval(timerinterval.current);
};
return (
<>
<button
onMouseDown={pressingDown}
onMouseUp={notPressingDown}
onTouchStart={pressingDown}
onTouchEnd={notPressingDown}
className="button is-primary mt-3"
>
Emergency
</button>
<br />
Time holding it is.... {ms}
</>
);
};
This is edited code (with some TypeScript stuff, sorry for that)

Moment JS subtract problems

I'm setting up a timer with moment, is not the only one I have in this screen but this doesn't work:
const halfBellTimer = () => {
const x = setInterval(() => {
let countTime = moment.duration().add({seconds: meditationTime / 2});
if (countTime <= 0) {
console.log('STOP');
} else {
countTime = countTime.subtract(1, 's');
console.log(countTime.seconds());
}
}, 1000);
};
It sets the time correctly but I get a log of the same value, so it doesn't subtract it.
Any idea?
Thanks!
If you move let countTime = moment.duration().add({seconds: meditationTime / 2});
outside of setInterval function it works fine.
Don't forget to clean up with clearInterval.
Take a look at the example.
const halfBellTimer = () => {
const meditationTime = 10;
let countTime = moment.duration().add({
seconds: meditationTime / 2
});
const x = setInterval(() => {
if (countTime <= 0) {
console.log('STOP');
clearInterval(x);
} else {
countTime = countTime.subtract(1, 's');
console.log(countTime.seconds());
}
}, 1000);
};
halfBellTimer();
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.19.1/moment.min.js"></script>

Can i call a action in every 20 second inside a timer which is always running in every second

I need to print a console in every 20 second(technically i will call a action in reactjs) inside a interval which is running in every second.
this is a dummy code not functioning properly.
var intervalDuration = 200;
var hitOnEvery = 20;
setInterval(() => {
interValDuration -= 1;
if (interValDuration >= 0) {
setTimeout(()=>{
console.log(interValDuration , 'Hit on every' + hitOnEvery + 'second')
},hitOnEvery);
}
console.log('This will print every '+intervalDuration +'count in a place')
}, 1000);
can someone correct and help me.
You can do it like this:
var interValDuration = 200;
var hitOnEvery = 20;
setInterval(() => {
interValDuration -= 1;
if (interValDuration % hitOnEvery == 0) {
console.log(interValDuration , 'Hit on every' + hitOnEvery + 'second')
}
console.log('This will print every '+interValDuration +'count in a place')
}, 1000);
Basically check if interValDuration divided by hitOnEvery has 0 leftover after division, meaning every 20sec perform action
Here is the closure function, for the generic usage
const timer = (() => {
let timer;
let seconds = 1;
return (func, hitAtTime) => {
let stopTimer = () => {
console.log(timer);
window.clearInterval(timer);
};
const startTimer = () => {
timer = window.setInterval(() => {
console.log("i run every second");
seconds++;
if (seconds % hitAtTime === 0) {
func();
}
}, 1000);
};
return {
stopTimer: stopTimer,
startTimer: startTimer,
};
};
})();
timer(() => console.log("i run every 10 seconds"), 10).startTimer();

Categories

Resources