Stop function is not working as expected. Need Help. React - javascript

So I'm new to React and I have made a timer that increases after clicking on Start. Similarly, I made a stop button to stop the timer and set the variable to 0. But what's happening is it starts again from 0.
Help would be appreciated.
Thanks in Advance.
import React from 'react';
class Timer extends React.Component {
constructor() {
super();
this.state = {
secondsElapsed: 0
};
}
start = () => {
this.setState({
secondsElapsed: this.state.secondsElapsed + 1
});
}
stop = () => {
this.setState({
secondsElapsed: 0
});
}
handleClick = (e) => {
this.interval = setInterval(this.start, 1000);
}
render() {
return ( <React.Fragment><br/>
<ul>
<li><span onClick = {this.handleClick}> Start Timer <br/><br/></span></li>
<li><span onClick = {this.stop}> Stop Timer </span></li>
</ul>
<Resultant new = {this.state.secondsElapsed}/>
</React.Fragment>);
}
}
class Resultant extends React.Component {
render() {
return ( <div>
<h3> Seconds Elapsed: {this.props.new} </h3>
</div>);
}
}
export default Timer;

You have to clear the interval to stop the function from running repeatedly:
stop = () => {
clearInterval(this.interval);
this.setState({
secondsElapsed: 0
});
}
For more details about timing events in JavaScript here.

In your stop(), you are only resetting the secondElapsed state back to 0. You also need to stop the interval from updating the state again.
You can try adding clearInterval(this.interval) in the stop().

Change your stop function to this:
stop = () => {
this.setState({
secondsElapsed: 0
});
clearInterval(this.interval); // You should clear the interval to stop it from incrementing seconds when "stop" is pressed
}

Problem
You are using setInterval by assigning it to a value named interval by this.interval = setInterval() so it starts doing that each second but later after stopping it still uses it because you didn't stop interval so it is making it in the "background".
Solution
You just need to add line with clearing interval in the beginning of your stop function like:
stop = () => {
clearInterval(this.interval)
// rest of code
}
so it will stop running that in the background even if you click on the stop button.

Related

How to execute an event in a loop until the event is true

I'm stuck in Javascript at looping a piece of code when an event listener is being
placed.
For example, let's say I have a div:
<div id="option">
</div>
and I added a Javascript mouseenter event listener:
const divItem = document.getElementById("option")
divItem.addEventListner("mouseenter", () => {
console.log("Mouse is entered")
})
Now the console log happens once and after I hover the mouse, but I want it to happen every after 4 seconds
and log the same message in the console until the mouse is moving out of the div.
I tried using setTimeout:
divItem.addEventListner("mouseenter", () => {
const timeoutEvent = () => {
console.log("Mouse is entered")
setTimeout( () => { timeoutEvent() }, 4000 )
}
timeoutEvent()
})
but it is logging even after the mouse left the div,
so how can I solve this?
You're on the right track. If you want every four seconds, you want to:
Use setInterval (or set a new setTimeout every time), and
Cancel it when you see mouseleave
const divItem = document.getElementById("option")
// The timer handle so we can cancel it
let timer = 0; // A real timer handle is never 0, so we can use it as a flag
divItem.addEventListener("mouseenter", () => {
console.log("Mouse is entered");
timer = setInterval(() => {
if (timer) {
console.log("Mouse is still here");
}
}, 1000);
})
divItem.addEventListener("mouseleave", () => {
console.log("Mouse left");
clearInterval(timer);
timer = 0;
});
<div id="option">
this is the div
</div>
(I've used one second in that example instead of four so it's easier to see it working.)
Or using setTimeout rescheduling itself instead:
const divItem = document.getElementById("option")
// The timer handle so we can cancel it
let timer = 0; // A real timer handle is never 0, so we can use it as a flag
const timerInterval = 1000;
function tick() {
if (timer) {
console.log("Mouse is still here");
timer = setTimeout(tick, timerInterval);
}
}
divItem.addEventListener("mouseenter", () => {
console.log("Mouse is entered");
timer = setTimeout(tick, timerInterval);
})
divItem.addEventListener("mouseleave", () => {
console.log("Mouse left");
clearTimeout(timer);
timer = 0;
});
<div id="option">
this is the div
</div>
You should use setInterval instead of setTimeout.
You can define your 'interval' function and the interval in global scope and then use them to set and start the interval execution and clearInterval ( stop de execution ) on mouseenter/mouseleave events.
const divItem = document.getElementById("option")
let myInterval;
const timeoutEvent = () => {
console.log("Mouse is entered")
}
divItem.addEventListener("mouseenter", () => {
myInterval = setInterval(timeoutEvent, 1000);
})
divItem.addEventListener("mouseleave", () => clearInterval(myInterval))
<div id="option">
My Option
</div>
divItem.addEventListener("mouseenter", () => {
console.log("Mouse is entered");
timer = setInterval(() => {
console.log("Mouse is still here");
}
}, 4000);
})

Is it possible to prevent a Timer ends?

I'm trying to show a label when a user clicks a button. I've tried to use setTimeout to achieve this, but when you click the button multiple times before the timeout ends, this don't work properly.
This is what I got:
const [cameraLabelVisible, setCameraLabelVisible] = useState(false);
let labelTimer;
function labelVisible() {
setCameraLabelVisible(true);
labelTimer = setTimeout(() => {
setCameraLabelVisible(false);
clearTimeout(labelTimer);
}, 1500);
}
};
My question is: Is it posible reset the timer to the initial value (in this case 1500) by clicking the same button before the timer ends?
I want to show the label if the button is clicked multiple times before the time runs out.
You could clear the existing timer first:
const [cameraLabelVisible, setCameraLabelVisible] = useState(false);
let labelTimer;
function labelVisible() {
setCameraLabelVisible(true);
// clear the timer if there's another timer running
if(labelTimer) clearTimeout(labelTimer);
labelTimer = setTimeout(() => {
setCameraLabelVisible(false);
}, 1500);
}
My question is: Is it possible reset the timer to the initial value
(in this case 1500) by clicking the same button before the timer ends?
Yes, this can be achieved by clearing the existing timeout and creating a new timeout. This can be achieved as below:
const [cameraLabelVisible, setCameraLabelVisible] = useState(false);
let labelTimer;
function labelVisible() {
if(labelTimer) {
clearTimeout(labelTimer);
}
setCameraLabelVisible(true);
labelTimer = setTimeout(() => {
setCameraLabelVisible(false);
clearTimeout(labelTimer);
}, 1500);
}
};
I want to show the label if the button is clicked multiple times
before the time runs out.
This sounds like a different issue than what you asked above. If I'm understanding you correctly, the below will allow you to click the button multiple times within 1.5 seconds, and the label appear for only that amount of time before clearing.
const [cameraLabelVisible, setCameraLabelVisible] = useState(false);
let labelTimer = undefined;
function labelVisible() {
setCameraLabelVisible(true);
if(!labelTimer) {
labelTimer = setTimeout(() => {
setCameraLabelVisible(false);
labelTimer = undefined;
}, 1500);
}
};

Idle timer within Vue Component

I am have some issues resetting my timer when no longer idle. I am using Vue Idle for this, which is a wrapper for idle.js.
So I have a modal with the id timeout-modal. When Vue Idle triggers the idle function, I call showWarningMessage.
Within this function, I first display my modal. I then create a timer which my modal uses to do a countdown. So this all works fine.
<script>
export default {
data() {
return {
timerId: 0,
remainingTimeoutSeconds: 10000
}
},
computed: {
second() {
return this.remainingTimeoutSeconds / 1000;
}
},
onIdle () {
this.showWarningMessage();
},
methods: {
showWarningMessage() {
this.$bvModal.show('timeout-modal');
this.warning = true;
this.timerId = setInterval(() => {
this.remainingTimeoutSeconds -= 1000;
}, 1000);
},
}
}
</script>
Now within the modal there is a continue button. When pressed, it should basically reset the above timer. At the moment I have
handleContinueButtonClick(response) {
if (response.data.success) {
console.log("IN")
this.$bvModal.hide('app-timeout-reminder-modal');
clearInterval(this.timerId);
return;
}
}
So what this should do is hide the modal, and then reset the timer back to 10 seconds. It is entering the above as the console is printing IN. The modal is also
hidden when I click OK.
However, the next time the modal is displayed, the timer is already near 0 as it did not reset back to 10.
Is there any reason why I cant get this back to 10 seconds? I thought clearInterval should reset the timer?
Thanks
I thought clearInterval should reset the timer?
Do you mean this.remainingTimeoutSeconds is set automatically when calling clearInterval?
The answer is no.
You need to reset that value as 10000 like blow;
handleContinueButtonClick(response) {
if (response.data.success) {
console.log("IN")
this.$bvModal.hide('app-timeout-reminder-modal');
this.remainingTimeoutSeconds = 10000;
clearInterval(this.timerId);
return;
}
}
or
showWarningMessage() {
this.$bvModal.show('timeout-modal');
this.warning = true;
this.remainingTimeoutSeconds = 10000;
this.timerId = setInterval(() => {
this.remainingTimeoutSeconds -= 1000;
}, 1000);
}

Function setIntervall become faster after change router

https://protected-temple-97157.herokuapp.com/
here theres my app, if you open you see that there no problems on slideshow, the image change after the 6 seconds, but if you go to other router and then comeback on Home after the first image the slideshow become more faster
componentDidMount() {
this.slide();
}
slide = () => {
$(".slideshow > .card:gt(0)").hide();
setInterval(() => {
$(".slideshow > .card:first")
.fadeOut(3000)
.next()
.fadeIn(3000)
.end()
.appendTo('.slideshow')
}, 6000)
}
I guess you add a new Interval each time you come back to the site. If you look closely you see that the time difference between the slides varies, therefore multiple intervals are set.
You can prevent this by calling setInterval only once, initially, or use clearInterval to clear the previous interval.
It's not that the slideshow is faster, but you will get another interval that changes slides because you don't stop the previous interval when the component is unmounted.
You can put the number returned from setInterval on the component and call clearInterval in componentWillUnmount to get around this.
Example
class App extends React.Component {
interval = null;
componentDidMount() {
this.slide();
}
componentWillUnmount() {
clearInterval(this.interval);
}
slide = () => {
$(".slideshow > .card:gt(0)").hide();
this.interval = setInterval(() => {
$(".slideshow > .card:first")
.fadeOut(3000)
.next()
.fadeIn(3000)
.end()
.appendTo(".slideshow");
}, 6000);
};
}

ReactJS ProgressBar value is not updating on par with state.progressValue

I'm currently using
import { Progress } from 'react-sweet-progress';
and before I also tried import { Progress } from 'reactstrap'; which just uses bootstrap 4 ProgressBar.
I have a state that maintains the progressValue, and using audio HTML tag to call in an online audio src and playing it. During timeupdate eventListener, I update my progressValue in my state, and reflect it back by setting the value of <Progress> as this.state.progressValue.
class FooBar extends Component {
state = {
progressValue: 0
}
handleProgress = () => {
this.currentTimeInterval = null;
this.audio.onplay = () => {
this.setState({
progressValue: 0,
play: true
});
this.audio.addEventListener('timeupdate', () => {
this.setState({
progressValue: Math.floor(this.audio.currentTime / this.audio.duration * 100)
}, function() {
console.log(this.state.progressValue);
})
}, true)
this.audio.onpause = () => {
clearInterval(this.currentTimeInterval);
}
}
render() {
return(
<audio
ref={(audio) => { this.audio = audio }}
src={http://www.music.helsinki.fi/tmt/opetus/uusmedia/esim/a2002011001-e02.wav}
autoPlay
onLoadedData={this.handleProgress}
/>
<Progress value={this.state.progressValue} />
);
}
}
The timing, however, doesn't seem to match up where the audio will be playing and the progressValue will be delayed in the sense that audio will be ahead of the progressValue. Hence, by the time audio finishes, it would likely take another 2~3 seconds for the progressBar to reach 100%.
I also tried:
this.currentTimeInterval = setInterval(() => {
this.setState({
progressValue: Math.floor(this.audio.currentTime / this.audio.duration * 100)
}))
}, 100)
and tried manipulating the timeInterval of 100ms to a smaller number, which makes it closer but the delay still exists.
3 Questions:
1) What is causing this to happen?
2) Is there a way to fix this?
3) If the answer is 'no' to 2), is there another component I can use to display the progress of an audio file? (Not the default controls from audio tag).
Thank you!

Categories