Remove Set interval on component unmount - javascript

This is my code for notification fetching
static contextType = Context;
constructor(props) {
super(props);
this.state = {
roleName: null,
notCount : null,
notificationData:[],
gotoNotify:false,
}
}
logOut = () => {
Cookies.remove('_token');
Cookies.remove('_ACid');
Cookies.remove('_ACrole');
Cookies.remove('_ACname');
}
componentDidMount() {
this.getNotification();
setInterval(() => {
this.getNotification();
}, 10000);
}
componentWillUnmount(){
clearInterval();
console.log("yes")
}
I want to clearinterval when this component will unmount or when logout please help me to do this

Keep track of the interval identifier and use it to clear it:
consructor(props) {
super(props);
this.state = {...}
this.notificationInterval = null; // unnecessary but good to keep track of it
}
componentDidMount() {
this.getNotification();
this.notificationInterval = setInterval(this.getNotification, 10000);
}
componentWillUnmount() {
clearInterval(this.notificationInterval);
}
As a small note, you can call setInterval(fn) directly without using the arrow function:
// The two calls are pretty much equal:
setInterval(() => this.getNotification(), 1000);
setInterval(this.getNotification, 10000);

You can use a global variable to assign your interval id.
After, you can clear your interval with this id
static contextType = Context;
let intervalId;
constructor(props) {
super(props);
this.state = {
roleName: null,
notCount: null,
notificationData: [],
gotoNotify: false,
}
}
logOut = () => {
clearInterval(intervalId);
Cookies.remove('_token');
Cookies.remove('_ACid');
Cookies.remove('_ACrole');
Cookies.remove('_ACname');
}
componentDidMount() {
this.getNotification();
clearInterval(intervalId);
intervalId = setInterval(() => {
this.getNotification();
}, 10000);
}
componentWillUnmount() {
clearInterval(intervalId);
console.log("yes")
}

Related

React setTimeout hooks onclick

It is giving an error Cannot read property 'handleCheck' of undefined when I click on next button. Can anyone please help?Thanks in advance
import React from "react";
import ReactDOM from "react-dom";
class App extends React.Component {
state = { check: false };
handleCheck = () => {
console.log("hello");
this.setState({ check: !this.state.check });
};
componentDidMount() {
setTimeout(() => {
this.handleCheck();
}, 10000);
}
timer() {
setTimeout(() => {
this.handleCheck();
}, 10000);
}
render() {
return (
<div>
<p>hello</p>
{this.state.check ? (
<button onClick={this.timer}>Next</button>
) : (
<div>button not showing </div>
)}
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("container"));
The timer should also be an arrow function to refer to the correct this:
timer = () => {
setTimeout(() => {
this.handleCheck();
}, 10000);
}
the other way to fix this would be to bind this to timer.
And since the new state depends on the old state, the handleCheck function should be like this:
handleCheck = () => {
console.log("hello");
this.setState(prevState => ({ check: !prevState.check }));
};
make it an arrow function:
timer = () => {
setTimeout(() => {
this.handleCheck();
}, 1000);
}
so it's bound to the parent scope
It's a binding issue of timer function :
timer = () => {
setTimeout(() => {
this.handleCheck();
}, 10000);
}
OR change onClick :
onClick={this.timer.bind(this)}
Solution :
class App extends React.Component {
state = { check: false };
handleCheck = () => {
console.log("hello");
this.setState({ check: !this.state.check });
};
componentDidMount() {
setTimeout(() => {
this.handleCheck();
}, 10000);
}
timer = () => {
setTimeout(() => {
this.handleCheck();
}, 10000);
}
render() {
return (
<div>
<p>hello</p>
{this.state.check ? (
<button onClick={this.timer}>Next</button>
) : (
<div>button not showing </div>
)}
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("react-root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.19.2/axios.min.js"></script>
<div id="react-root"></div>
You need to bind this to the timer function.
<button onClick={this.timer.bind(this)}>Next</button>
You can use the arrow function as other users said, or as alternative you can manually bind this to the function:
// in the button
<button onClick={this.timer.bind(this)}>Next</button>
// or in the constructor
constructor(props) {
super(props)
this.timer = this.timer.bind(this)
}
<button onClick={this.timer)}>Next</button>
hi as previous people said you need to bind (this) one of the way is to do it like this
class App extends React.Component {
constructor(props) {
super(props);
this.state = { check: false };
// This binding is necessary to make `this` work in the callback
this.handleCheck = this.handleCheck.bind(this);
}
this is happens because when you enter a function the class this can't be reach
bind solve this in regular function
when you go with arrow function this scope don't use there on this scope instead they inherit the one from the parent scope
like this:
instead of:
timer() {
setTimeout(() => {
this.handleCheck();
}, 10000);
}
do this:
timer = () => {
setTimeout(() => {
this.handleCheck();
}, 10000);
}

Is there a way to refresh the browser page every 15 mins if it has been idle for 15 mins using reactjs

intervalID = setInterval(() => window.location.reload(true), 15000 );
I have tried above logic it is working fine but i need a logic in reactjs that refresh window page when user is idle
In case you are using class based components. you could make use of event listeners for tracking the user activity as follows:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
isIdle: false
}
this.idleTime = 0;
this.handleTimer = this.handleTimer.bind(this);
this.resetTimer = this.resetTimer.bind(this);
}
componentDidMount() {
document.documentElement.addEventListener('mousemove', this.resetTimer);
document.documentElement.addEventListener('keypress', this.resetTimer);
this.idleInterval = setInterval(this.handleTimer, 1000);
}
componentWillUnmount() {
document.documentElement.removeEventListener('mousemove', this.resetTimer);
document.documentElement.removeEventListener('keypress', this.resetTimer);
clearInterval(this.idleInterval)
}
resetTimer() {
this.idleTime = 0;
this.setState({isIdle: false})
}
handleTimer() {
this.idleTime = this.idleTime + 1;
if (this.idleTime > 10) {
this.handleIdle();
}
}
handleIdle() {
// Refresh your page here
this.setState({isIdle: true})
}
render() {
return(
<div>
<h1 className="demo">React Template</h1>
{this.state.isIdle &&
<p>You have been idle for {this.idleTime} seconds</p>
}
</div>
)
}
};
ReactDOM.render(
<App />,
document.getElementById("app")
);
try below the code, edit.
useEffect(() => {
const check = ()=> {
if (!document.hasFocus()) {
window.location.reload()
}
}
setInterval(check, 1000*60*15)
})
or one line.
useEffect(() => {
setInterval(()=> !document.hasFocus() ? document.location.reload(): '', 1000*60*15)
})
please let me know if you need this solution.

Restarting setInterval after clearInterval in React

In this game I'm building, I clear the setInterval after the user loses. When they click Play Again? I want the timer to start again, but I'm having trouble making that happen while using React. I've tried several things like separating the timer into its own component, making a helper function, and using the life cycle methods, but I just can't seem to get this part. I can start and clear the setInterval just fine, but it's restarting it that's my problem here.
import React, {Component} from 'react';
// helper function to set a random action
function setRandomAction() {
let actions = ['bop it', 'pull it', 'twist it'];
let rando = actions[Math.floor(Math.random() * actions.length)];
return rando;
}
class BopIt extends Component {
constructor(props) {
super(props);
// set initial action in this.state so it is not empty on pageload
this.state = {
action: setRandomAction(),
countdown: 3,
userPressed: '',
play: true
}
this.bind = this.keyPressed.bind(this);
this.bind = this.keepPlaying.bind(this);
this.bind = this.timer.bind(this);
this.bind = this.startTimer.bind(this);
this.bind = this.endGame.bind(this);
this.quitGame = this.quitGame.bind(this);
this.playAgain = this.playAgain.bind(this);
}
componentDidMount() {
this.keyPressed();
this.startTimer();
}
startTimer() {
let setTimerTime = parseInt(`${this.state.countdown - 2}000`);
this.stopIntervalId = setInterval(() => this.timer(), setTimerTime);
}
componentWillUnmount() {
this.startTimer();
this.keyPressed();
this.keepPlaying();
this.endGame();
}
timer() {
var count = this.state.countdown;
if (count === 0) {
count = 4
}
this.setState({countdown: count - 1});
}
keyPressed() {
document.addEventListener('keyup', (e) => {
if (e.key === 'ArrowLeft') {
this.setState({
userPressed: 'pull it'
});
} else if (e.key === 'ArrowDown') {
this.setState({
userPressed: 'bop it'
});
} else if (e.key === 'ArrowRight') {
this.setState({
userPressed: 'twist it'
});
} else {
// this.endGame();
this.setState({
userPressed: 'wrong'
});
}
if (this.state.userPressed !== this.state.action) {
this.endGame();
} else {
this.keepPlaying();
}
});
}
keepPlaying() {
let actions = ['bop it', 'pull it', 'twist it'];
let rando = actions[Math.floor(Math.random() * actions.length)];
this.setState({
action: rando,
userPressed: ''
});
}
endGame() {
console.log('You Lost!!!');
this.setState({
play: false
});
}
quitGame() {
clearInterval(this.stopIntervalId);
}
playAgain() {
this.setState({
play: true,
action: setRandomAction(),
countdown: 3
});
}
render() {
// if (this.state.countdown <= 0) {
// this.endGame();
// }
console.log(this.state)
let gameAction = `${this.state.action} ${this.state.countdown}`;
return (
<div className="bop-it">
<div className="show-action">
{this.state.play ? gameAction : <ResetGame playAgain={this.playAgain} quitGame={this.quitGame}/> }
</div>
<span>Pull It</span>
<br/>
<span>Bop It</span>
<br/>
<span>Twist It</span>
</div>
);
}
}
class ResetGame extends Component {
render() {
return (
<div>
<input type="button" value="Play Again?" onClick={this.props.playAgain}/>
<input type="button" value="Quit Game?" onClick={this.props.quitGame}/>
</div>
);
}
}
export default BopIt
EDIT:
I ended up simply calling this.startTimer() at the end of the playAgain() method. I could have sworn I did that previously, but apparently not. I'm also making sure to only call clearInterval in one place so there are no conflicts in other parts of the app.
Another issue I was having was that whenever I got setInterval to restart, the timer was counting down at a faster rate. That was because of this:
let setTimerTime = parseInt(`${this.state.countdown - 2}000`);
this.stopIntervalId = setInterval(() => this.timer(), setTimerTime);
I put that line of code there because I eventually want the user to choose the game speed, but this was totally messing up the countdown property in my app's state object. Removing this line for now has cleared up some confusion too.
import React, {Component} from 'react';
// helper function to set a random action
function setRandomAction() {
let actions = ['bop it', 'pull it', 'twist it'];
let rando = actions[Math.floor(Math.random() * actions.length)];
return rando;
}
class BopIt extends Component {
constructor(props) {
super(props);
// set initial action in this.state so it is not empty on pageload
this.state = {
action: setRandomAction(),
countdown: 3,
userPressed: '',
play: true
}
this.bind = this.keyPressed.bind(this);
this.bind = this.keepPlaying.bind(this);
this.bind = this.endGame.bind(this);
this.bind = this.timer.bind(this);
this.bind = this.startTimer.bind(this);
this.quitGame = this.quitGame.bind(this);
this.playAgain = this.playAgain.bind(this);
}
componentDidMount() {
this.keyPressed();
this.startTimer();
}
startTimer() {
// let setTimerTime = parseInt(`${this.state.countdown - 2}000`);
this.stopIntervalId = setInterval(() => this.timer(), 1000);
}
componentWillUnmount() {
this.keyPressed();
this.keepPlaying();
this.endGame();
}
timer() {
let count = this.state.countdown;
if (count === 0) {
count = 4
// end the game if the timer hits 0
this.endGame();
}
this.setState({countdown: count - 1});
}
keyPressed() {
document.addEventListener('keyup', (e) => {
if (e.key === 'ArrowLeft') {
this.setState({
userPressed: 'pull it'
});
} else if (e.key === 'ArrowDown') {
this.setState({
userPressed: 'bop it'
});
} else if (e.key === 'ArrowRight') {
this.setState({
userPressed: 'twist it'
});
} else {
this.setState({
userPressed: 'wrong'
});
}
// if user presses wrong key, then the game is over
if (this.state.userPressed !== this.state.action) {
this.endGame();
} else {
// otherwise, reset the time and chooose a random action
this.keepPlaying();
}
});
}
keepPlaying() {
this.setState({
action: setRandomAction(),
countdown: 3,
userPressed: ''
});
}
endGame() {
console.log('You Lost!!!');
this.setState({
play: false
});
clearInterval(this.stopIntervalId);
}
quitGame() {
// clearInterval(this.stopIntervalId);
console.log('you have left the game')
}
playAgain() {
this.setState({
play: true,
action: setRandomAction(),
countdown: 3
});
this.startTimer();
}
render() {
let gameAction = `${this.state.action} ${this.state.countdown}`;
return (
<div className="bop-it">
<div className="show-action">
{this.state.play ? gameAction :
<ResetGame
playAgain={this.playAgain}
quitGame={this.quitGame}
/>
}
</div>
<span>Pull It</span>
<br/>
<span>Bop It</span>
<br/>
<span>Twist It</span>
</div>
);
}
}
class ResetGame extends Component {
render() {
return (
<div>
<input type="button" value="Play Again?" onClick={this.props.playAgain}/>
<input type="button" value="Quit Game?" onClick={this.props.quitGame}/>
</div>
);
}
}
export default BopIt
I think the problem might be that you are setting 2 timers, at the same time, one in componentWillUnmount and another one in componentWillMount and when you delete them you only delete one of them because the other gets lost by variable overwritting
I would change the following methods on your code, so that it's a little more difficult to create duplicate timers and so that it also triggers the timer again after playing again
startTimer() {
let setTimerTime = parseInt(`${this.state.countdown - 2}000`);
if (!this.stopIntervalId) {
this.stopIntervalId = setInterval(() => this.timer(), setTimerTime);
}
}
quitGame() {
clearInterval(this.stopIntervalId);
this.stopIntervalId = undefined;
}
playAgain() {
this.setState({
play: true,
action: setRandomAction(),
countdown: 3
});
this.startTimer()
}

Using setTimeout with React and Redux

I've got some animations that I'm trying to get to work using setTimeouts and for some reason they are firing over and over again until the end of time. I've got a reducer that holds all of my booleans and an action that toggles them, but the problem is that the action is being fired regardless of whether or not the condition is true in the setTimeouts. I've looked in the chrome console and confirmed this to be true, but I don't know why. I'll place my code below.
type LandingPagePropTypes = {
displayCommandText: boolean,
displayInstallText: boolean,
displayAboutText: boolean,
displayEnterText: boolean,
displayWelcomeHeader: boolean,
togglePropertyInState: (propertyName: string) => void,
togglePopUpModal: (message: string) => void,
};
const LandingPage = (
{
displayWelcomeHeader,
displayCommandText,
displayAboutText,
displayInstallText,
displayEnterText,
togglePropertyInState,
togglePopUpModal,
}: LandingPagePropTypes,
) => {
setTimeout(() => {
if (!displayCommandText) {
togglePropertyInState('displayCommandText');
}
}, 1000);
setTimeout(() => {
if (!displayInstallText) {
togglePropertyInState('displayInstallText');
}
}, 3000);
setTimeout(() => {
if (!displayAboutText) {
togglePropertyInState('displayAboutText');
}
}, 4000);
setTimeout(() => {
if (!displayEnterText) {
togglePropertyInState('displayEnterText');
}
}, 5000);
setTimeout(() => {
if (!displayWelcomeHeader) {
togglePropertyInState('displayWelcomeHeader');
}
}, 1000);
return (
<div className="landing-page-container">
<MediaQuery maxWidth={767}>
<MobileLandingPage
displayWelcomeHeader={displayWelcomeHeader}
/>
</MediaQuery>
<MediaQuery minWidth={768}>
<DesktopLandingPage
displayCommandText={displayCommandText}
displayInstallText={displayInstallText}
displayAboutText={displayAboutText}
displayEnterText={displayEnterText}
togglePopUpModal={togglePopUpModal}
/>
</MediaQuery>
</div>
);
};
setTimeout() belongs in the componentDidMount or componentDidUpdate methods. You will also need a clearTimeout in the componentWillUnmount method to cancel the timeout or you'll get a setState on an unmounted component warning if you unmount the component before the timeout fires. Here's a simplified example.
class SomeComp extends Component {
constructor() {...}
_startAnimation = timeout => {
this.enterAnimation = setTimeout(
() => this.setState({ mode: 'entered' }),
timeout
)
}
componentDidMount() {
const timeout = someNum
this._startAnimation(timeout)
}
componentWillUnmount() {
!!this.enterAnimation && clearTimeout(this.enterAnimation)
}
render() {...}
}
I wanted to update on what I ended up doing. I want to add that I'm using Flowtype and eslint paired with AirBnB rules so I had to restructure things a bit to satisfy both of them.
class LandingPage extends Component <LandingPagePropTypes> {
constructor(props: LandingPagePropTypes) {
super(props);
const { togglePropertyInState } = this.props;
this.setCommandText = setTimeout(() => togglePropertyInState(
'displayCommandText'
), 1000);
this.setInstallText = setTimeout(() => togglePropertyInState(
'displayInstallText'
), 3000);
this.setAboutText = setTimeout(() => togglePropertyInState(
'displayAboutText'
), 4000);
this.setEnterText = setTimeout(() => togglePropertyInState(
'displayEnterText'
), 5000);
this.setWelcomeHeader = setTimeout(() => togglePropertyInState(
'displayWelcomeHeader'
), 1000);
}
componentWillUnmount() {
const {
displayCommandText,
displayInstallText,
displayAboutText,
displayEnterText,
displayWelcomeHeader,
} = this.props;
if (displayCommandText) {
clearTimeout(this.setCommandText);
}
if (displayInstallText) {
clearTimeout(this.setInstallText);
}
if (displayAboutText) {
clearTimeout(this.setAboutText);
}
if (displayEnterText) {
clearTimeout(this.setEnterText);
}
if (displayWelcomeHeader) {
clearTimeout(this.setWelcomeHeader);
}
}
setCommandText: TimeoutID;
setInstallText: TimeoutID;
setAboutText: TimeoutID;
setEnterText: TimeoutID;
setWelcomeHeader: TimeoutID;
render() {
const {
displayWelcomeHeader,
displayCommandText,
displayAboutText,
displayInstallText,
displayEnterText,
togglePopUpModal,
} = this.props;
return (
<div className="landing-page-container">
<MediaQuery maxWidth={767}>
<MobileLandingPage
displayWelcomeHeader={displayWelcomeHeader}
/>
</MediaQuery>
<MediaQuery minWidth={768}>
<DesktopLandingPage
displayCommandText={displayCommandText}
displayInstallText={displayInstallText}
displayAboutText={displayAboutText}
displayEnterText={displayEnterText}
togglePopUpModal={togglePopUpModal}
/>
</MediaQuery>
</div>
);
}
}

Updating state of a timer with an interval

I started to learn React and in the frontpage examples, it is shown how to make a simple timer component:
class Timer extends React.Component {
constructor(props) {
super(props);
this.state = { seconds: 0 };
}
tick() {
this.setState(prevState => ({
seconds: prevState.seconds + 1
}));
}
componentDidMount() {
this.interval = setInterval(() => this.tick(), 1000);
}
componentWillUnmount() {
clearInterval(this.interval);
}
render() {
return (
<div>
Seconds: {this.state.seconds}
</div>
);
}
}
ReactDOM.render(<Timer />, mountNode);
Everything is pretty clear, except this line (componentDidMount method):
this.interval = setInterval(() => this.tick(), 1000);
Why can't I directly write:
this.interval = setInterval(this.tick, 1000);
I get the following error:
TypeError: this.setState is not a function
In your constructor method you have to bind the context to tick method:
constructor(props) {
super(props);
this.state = { seconds: 0 };
this.tick = this.tick.bind(this);
}
Please read Event Handling in React
Below you have a working example with the simplified version you wanted. You need to have
this.tick.bind(this)
class Timer extends React.Component {
constructor(props) {
super(props);
this.state = { seconds: 0 };
}
tick() {
this.setState(prevState => ({
seconds: prevState.seconds + 1
}));
}
componentDidMount() {
this.interval = setInterval(this.tick.bind(this), 1000);
}
componentWillUnmount() {
clearInterval(this.interval);
}
render() {
return (
<div>
Seconds: {this.state.seconds}
</div>
);
}
}
ReactDOM.render(<Timer />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>

Categories