I am new to Angular(6). I am using setInterval function in a component. It is working but when I navigate to another route, setInterval continues to execute. Please help me to identify the reason.
//Calling it in ngOnit()
autosavedraftsolution() {
setInterval(() => {
console.log(this.draftSolutionForm);
if (this.solutionTitleValid) {
this.savedraftsolution();
}
}, this.autoSaveInterval);
}
//savedraftsolution()
savedraftsolution() {
console.log("saving..");
this.connectService.saveDraftSolution({
Title: this.draftSolutionForm.get('Title').value,
Product: this.draftSolutionForm.get('Product').value
} as Draftsolution).subscribe(draftsol => {
console.log("saved");
});
}
It keeps on showing me "saving.." and "saved" message in console.
You need to call clearInterval to stop it when your component unmounts:
this.intervalId = setInterval(...);
When your component is unmounting:
ngOnDestroy() {
clearInterval(this.intervalId);
}
Dominic is right. You have to clear the interval when the component is destroyed. You can make something like this
ngOnInit(){
this.saveInterval = setInterval(() => {}, 1000)
}
ngOnDestroy(){
clearInterval(this.saveInterval)
}
Make sure that your component implements OnInit and OnDestroy.
Related
I want to update the value of a counter variable after some fixed time indefinitely. Here is my code:
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = {
myCount: 1
};
}
// Update state after every 15 seconds.
setTimeout(function(){
this.setState({myCount: this.state.myCount + 1});
}, 15000);
}
However, I get error about unexpected token with this component. How can I set state within the class properly without using any event listeners?
Thanks.
Your call to setTimeout is at the top level of the class body where properties, the constructor, and methods are expected. You can't put arbitrary code there. (Though you could could have a property initializer; not a good idea in this case, though.)
Put it in componentDidMount; and be sure to saved the timer handle (on the instance, usually) and clear the timer in componentWillUnmount so it doesn't fire if the component is unmounted before the timer callback occurs:
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = {
myCount: 1,
};
}
componentDidMount() {
// Update state after every 15 seconds.
this.timerHandle = setTimeout(() => { // ****
this.setState((previousState) => { // ****
return { myCount: previousState.myCount + 1 }; // ****
}); // ****
}, 15000);
}
componentWillUnmount() {
clearTimeout(this.timerHandle);
}
}
Side note: Notice the changes on the **** lines above
I replaced your traditional function with an arrow function so it closes over this.
I changed your callback to use the callback version of setState, which is generally best when setting state based on existing state.
...after some fixed time indefinitely.
A setTimeout callback will only occur once. You may want setInterval if you want the counter updated every 15 seconds. Or alternatively, start a new setTimeout when the callback runs, like this:
startCounterTimer() {
// Update state after every 15 seconds.
this.timerHandle = setTimeout(() => {
this.setState((previousState) => {
this.startCounterTimer(); // ***
return { myCount: previousState.myCount + 1 };
});
}, 15000);
}
componentDidMount() {
this.startCounterTimer();
}
I have made a basic slideshow demo where on checking the checkbox slideshow is enabled. Problem is that once enabled slide show can't be disabled even if I uncheck the checkbox. As per muy understanding, I'm learning the timer and also nullifying the state storing the timer but the slide show keep on going.
Specifically this part gets invoked on checkbox update:
useEffect(() => {
if (isTrue) {
setSlideTimer(() => {
return setInterval(() => {
forwardButton.current.click();
}, slideDuration);
});
} else {
clearInterval(slideTimer);
setSlideTimer(null);
}
}, [isTrue]);
From browser logs it is evident that timer indeed got cleared. Though there is a warning "... component is changing an uncontrolled input of type checkbox to be controlled" but I'm not sure if that's the culprit here.
Issue
The issue is that you've a missing dependency on the sliderTimer state.
useEffect(() => {
if (isTrue) {
setSlideTimer(() => {
return setInterval(() => {
forwardButton.current.click();
}, slideDuration);
});
} else {
clearInterval(slideTimer);
setSlideTimer(null);
}
}, [isTrue]);
Solution
Don't generally store timer ids in state. Use a React ref is you need to access the timer id outside the useEffect hook, otherwise just cache it locally within the useEffect hook's callback. In this case you will want to use a ref to hold the sliderTimer id value so it can also be cleared in the case the component unmounts.
Example:
const sliderTimerRef = React.useRef();
useEffect(() => {
// Clear any running intervals on component unmount
return () => clearInterval(sliderTimerRef.current);
}, []);
useEffect(() => {
if (isTrue && forwardButton.current) {
sliderTimerRef.current = setInterval(
forwardButton.current.click,
slideDuration
);
} else {
clearInterval(sliderTimerRef.current);
}
}, [isTrue]);
Additional issue
From browser logs it is evident that timer indeed got cleared. Though
there is a warning "... component is changing an uncontrolled input of type checkbox to be controlled" but I'm not sure if that's the
culprit here.
This is typically the case when the value or checked prop changes from an undefined to a defined value. Ensure whatever the checked state is that it is initially defined, even if just false.
Try this!
useEffect(() => {
let interval;
if (isChecked) {
interval = setInterval(() => {
forwardButton.current.click();
}, 1000); // change it
}
return function cleanup() {
clearInterval(interval);
console.log('Clear timer');
};
}, [isChecked]);
Another approach is to use the setTimeout() method. For this you would need to create a new state clicks (or any other name) that will trigger the useEffect every time it changes, allowing setTimeout to work as setInterval
const [clicks, setClicks] = useState(0)
useEffect(() => {
if(isChecked){
setTimeout(() => {
forwardButton.current.click()
setClicks(clicks + 1)
}, slideDuration)
}
}, [isChecked, clicks])
I have a service which gets returns some data at the page load,
getAllTurbinesStat(){
return this.http.get(this.url_stat_Turbines_all);
}
in my component I consume this service:
this.service.getAllTurbinesStat().subscribe( s => {
this.allStats.push(s);
});
allStat is an array, now every 3 minutes this function should be run to update data, should setInterval be in service or in my component? And how should I write it? Because the first time I don't need the setinterval, because once the page loaded first time, my data is updated.
You can try this.
First create one function in your component like this.
getAllTurbinesStat() {
this.service.getAllTurbinesStat().subscribe(s => {
this.allStats.push(s);
});
}
And then in from ngOnInit() or from constructor of your component use this.
this.getAllTurbinesStat();
setInterval(() => this.getAllTurbinesStat(), 180000);
You can use interval for that like below:
in you service
getAllTurbinesStat(){
return this.http.get(this.url_stat_Turbines_all);
}
getData(): {
return interval(3000).pipe(
switchMap( () => this.getAllTurbinesStat())
)
}
in your component
this.service.getData().subscribe( s => {
this.allStats.push(s);
});
You can use timer from rxjs.
import { timer } from 'rxjs';
/*
timer takes a second argument, how often to emit subsequent values
in this case we will emit first value after 0 second and subsequent
values every 3 minutes after
*/
const source = timer(0, 180000);
//output: 0,1,2,3,4,5......
const subscribe = source.subscribe(val => console.log(val));
For you case.
return timer(0, 180000).pipe(
flatMap( () => this.getAllTurbinesStat())
)
First call your function one time in the ngOnInit of your component then get on with setInterval.
ngOnInit() {
getAllTurbinesStat();
setInterval(getAllTurbinesStat(), 3000);
}
getAllTurbinesStat() {
return this.http.get(this.url_stat_Turbines_all);
}
I need to call the backend function in angular's component according to the timer.
I tried 2 options:
Option N1:
_api: Service;
constructor(api: Service){
this._api = api;
if (this.timeout != null) {
clearTimeout(this.timeout);
}
this.timeout = setTimeout(() => {
this.update();
}, 20000);
}
update()
{
this._api.GetData()….
}
The Result of this implementation does not run timer every N seconds, it calls update() function only once. But with this approach I can access the methods/properties of the this.api
OPTION N2:
_api: Service;
constructor(api: Service){
this._api = api;
setInterval(this.update, 2000);
}
update()
{
this._api.GetData()….
}
And the result is: for some reason it can't see the methods/properties of this._api.
I checked whether I can see them if I don't use setInterval and it seems that the problem is caused by setInterval.
Please advise how to arrange timer work of some method in Angular?
You are correct in using setInterval to run the chunk every n seconds, but you need to use arrow function notation to refer to the class member variables using this keyword. Try the following
constructor(api: Service) {
this._api = api;
setInterval(() => { this.update() }, 2000); // <-- use arrow function here
}
update() {
this._api.GetData()….
}
But what is the purpose of using an additional variable to refer to the injected service? You could directly refer to it.
constructor(api: Service) {
setInterval(() => { this.update() }, 2000);
}
update() {
this.api.GetData()….
}
Furthermore, I presume you are making some HTTP request in the GetData() function. In that case, it would be better to cancel any impending requests before triggering a new one in the interval.
dataSubscription: any;
constructor(api: Service) {
setInterval(() => { this.update() }, 2000);
}
update() {
if (this.dataSubscription) {
this.dataSubscription.unsubscribe(); // <-- unsubscribing cancels any impending requests
}
this.dataSubscription = this.api.GetData()….
}
How about interval function provided by rxjs?
class Service {
getData() {
return rxjs.of( Array(Math.floor(Math.random() * 5)).fill(0).map((pr, index) => ({id: index})))
}
}
class Component {
constructor(service) {
this.service = service;
this.observable = null;
this.ngOnInit = this.ngOnInit.bind(this);
}
ngOnInit() {
this.observable = rxjs.interval(3000)
.pipe(rxjs.operators.mergeMap(_ => this.service.getData()));
}
}
const service = new Service();
const component = new Component(service);
component.ngOnInit();
component.observable.subscribe(values => {
console.log(values)
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.5.5/rxjs.umd.js"></script>
Alright, So I have a Splash screen which uses setInterval but the problem is setInterval now basically affects my whole navigation in my app I tried to use clearInterval but It won't work.
I tried changing from componentWillMount to componentDidMount but it still doesn't work.
componentDidMount(){
const splashInterval = setInterval(()=>{
this.props.navigation.navigate('Main');
},2000);
this.setState({splashInterval: splashInterval});
}
componenWillUnmount(){
const splashInterval = this.state.splashInterval;
const clearSplashInterval = this.props.navigation.clearInterval(splashInterval);
this.setState(clearSplashInterval);
}
you don't need to clear interval you can just simply
componentDidMount(){
setTimeout(() => {
this.props.navigation.navigate("Main")
}, 100);
}
when you are navigate to another class you can use this and if you want to reset your stack like you don't want splash screen in stack
componentDidMount(){
setTimeout(() => {
const resetAction = StackActions.reset({
index: 0,
actions: [NavigationActions.navigate({ routeName: 'main' })],
});
this.props.navigation.dispatch(resetAction);
}, 100);
}
Why are you getting the clearInterval from the props?????
to clear the setInterval
do this
componentDidMount(){
this.inter = setInterval(async() => {
this.props.navigation.navigate('Main');
}, 2000);
}
componenWillUnmount(){
clearInterval(this.inter);
}
The clearInterval() function clears the interval which has been set by setInterval() function before that.