I've been trying to get the countDown() function to run automatically inside render() function, but can't seem to figure it out. Here's the code:
import React from 'react';
import ReactDOM from 'react-dom';
class Counter extends React.Component {
constructor(props) {
super(props);
this.countDown = this.countDown.bind(this);
this.state = {
count: 5,
message: ''
}
}
countDown() {
setInterval(() => {
if (this.state.count <= 0) {
clearInterval(this);
this.setState(() => {
return {message: "Click here to skip this ad"}
})
} else {
this.setState((prevState) => {
return {count: prevState.count - 1}
})
}
}, 1000)
}
render() {
return (
<div>
<h1 onLoad={this.countDown}>
{this.state.message ? this.state.message : this.state.count}
</h1>
</div>
)
}
}
ReactDOM.render(<Counter />, document.getElementById('app'));
I'm not even sure if this is the optimal way to do it. My goal was to have a 5-second countdown displayed on screen then replace it with the download message/link when the countdown hits zero.
Use componentDidMount for starting the interval and clear it (to be sure) in componentWillUnmount too.
Then use the this.setState correctly
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 5,
message: ''
}
}
componentDidMount() {
this.inter = setInterval(() => {
if (this.state.count <= 0) {
clearInterval(this.inter);
this.setState({
message: 'Click here to skip this ad'
});
} else {
this.setState((prevState) => ({count: prevState.count - 1}));
}
}, 1000);
}
componentWillUnmount() {
clearInterval(this.inter);
}
render() {
return (
<div>
<h1>
{this.state.message ? this.state.message : this.state.count}
</h1>
</div>
)
}
}
ReactDOM.render(<Counter />, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="app"></div>
I would recommend calling countDown in componentDidMount also it is recommended to store the interval and clear it anyway in componentWillUnmount.
As is your countdown method will run indefinitely as you know is mostly the case with SetIntervals. Also try to avoid using onLoads to call event handlers. What you should do is make use of the component life cycle methods provided by React. Specifically ComponentDidMount() and ComponentDidUpdate() in your case.
For your countdown, try using something like this
class Clock extends React.Component {
state = {
counter: 10
}
//immediately is triggered. This only happens once. And we have it immediately call our countdown
componentDidMount() {
this.countDown()
}
countDown = () => {
this.setState((prevState) => {
return{
counter: prevState.counter - 1
}
})
}
//will get called everyt time we update the component state
componentDidUpdate(){
if(this.state.counter > 0){
setTimeout(this.countDown, 1000) //calls the function that updates our component state, thus creating a loop effect
}
}
render() {
return (
<div className="time">
The time is: {this.state.counter}
</div>
);
}
}
Related
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);
}
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.
I have a Timer component in a returned from a TimerContainer
const Timer = ({ time = 0 }) => (
<div className={styles.timer}>{formatTime(time)}</div>
);
Timer.propTypes = {
time: PropTypes.number
};
class TimerContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
secondsElapsed: 0
};
}
componentDidMount() {
this.interval = setInterval(this.tick.bind(this), 1000);
}
componentWillUnmount() {
clearInterval(this.interval);
}
tick() {
this.setState({
secondsElapsed: this.state.secondsElapsed + 1
});
}
render() {
return <Timer time={this.state.secondsElapsed} />;
}
}
How do I get it to only start when I click on another Button component? In my main component I have two functions for the Buttons
handleEasyCards() {
this.setState(prevState => ({ currentCards: this.state.easyCards }));
}
handleHardCards() {
this.setState({ currentCards: this.state.hardCards });
}
render() {
return (
<div style={boardContainer}>
<div style={buttonContainer}>
<Button
difficulty={this.state.easyButton}
onClick={this.handleEasyCards}
/>
<Button
difficulty={this.state.hardButton}
onClick={this.handleHardCards}
/>
</div>
<Cards
cardTypes={this.state.currentCards}
removeMatches={this.removeMatches}
/>
</div>
);
}
}
I think I need to pass a callback to the Button component and call it in the handleHardCards and handleEasyCards. I don't think this is a conditional render because the Timer will start with either Button clicked.
You could have another variable in the state:
constructor(props) {
super(props);
this.state = {
secondsElapsed: 0,
isCountingTime: false,
};
}
Then change that variable when an event happen:
handleEasyCards() {
this.setState(prevState => ({
currentCards: this.state.easyCards,
isCountingTime: true,
}));
}
handleHardCards() {
this.setState({
currentCards: this.state.hardCards,
isCountingTime: true,
});
}
Until now Timer has not been mounted so have not started counting. But with isCountingTime set to true it will render and start counting:
<div style={boardContainer}>
{this.state.isCountingTime && <Timer />}
...
</div>
The good part is that you can "start" and "reset" Timer whenever you want just by changing isCountingTime variable to true. The bad part is that nothing is rendered (no default values) when is set to false.
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>
I am getting this error setState(...): Can only update a mounted or mounting component. but I can't work out how to fix it.
import React, { Component } from 'react';
import Loading1 from '../images/loading1.gif';
class LoadingSpinner extends Component {
constructor(props){
super(props);
this.changeState = this.changeState.bind(this);
this.timer = this.timer.bind(this);
}
state = {
loadingImg: Loading1,
loading: true
}
timer(){
var self = this;
var startTime = new Date().getTime();
var interval = setInterval(function () {
if (new Date().getTime() - startTime > 3000) {
clearInterval(interval);
return;
}
self.changeState();
}, 2000);
}
changeState(){
this.setState({
loading: false,
})
}
render() {
const topMargin = {
marginTop: "50px"
}
return (
<div className="containter" style={topMargin}>
<center>
{this.state.loading ? <img src={this.state.loadingImg} onLoad= {this.timer()} alt="Loading..." /> : <h2>Unable To Find What You Are Looking For!</h2> }
</center>
</div>
);
}
}
export default LoadingSpinner;
this is my code that is causing the issue.
Basically I want it so that after the set amount of time it will change from the loading1.gif to say Unable to find what you are looking for. It does this but it throws the error setState(...): Can only update a mounted or mounting component. which I can't get rid of.
This is how I am calling loading spinner
<Tab label="Applicant Details" value="GetCasePersonal">
{this.state.GetCasePersonal.length === 0 ? <LoadingSpinner /> :
<div>
<ViewPersonalDetails Case={this.state.GetCasePersonal} />
</div>
}
</Tab>
You need not use a setInterval function, you can do it with setTimeout easily and you should use a React lifecycle function to have a timeout instead of calling it onLoad of image.
class LoadingSpinner extends React.Component {
constructor(props){
super(props);
this.timeout = null;
this.changeState = this.changeState.bind(this);
}
state = {
loadingImg: '',
loading: true
}
componentDidMount(){
this.timeout = setTimeout( () => {
console.log('I am changing state');
this.changeState();
}, 3000);
}
componentWillUnmount() {
clearTimeout(this.timeout);
}
changeState(){
this.setState({
loading: false,
})
}
render() {
const topMargin = {
marginTop: "50px"
}
return (
<div className="containter" style={topMargin}>
<center>
{this.state.loading ? <img src={this.state.loadingImg} alt="Loading..." /> : <h2>Unable To Find What You Are Looking For!</h2> }
</center>
</div>
);
}
}
ReactDOM.render(<LoadingSpinner/>, document.getElementById('app'));
<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="app"></div>
There's good documentation about this here
https://facebook.github.io/react/blog/2015/12/16/ismounted-antipattern.html
When dealing with such problems a good practice is to search in react facebook documentation.
private _isMount = false;
componentDidMount() {
this._isMount = true;
}
componentWillUnmount()
{
this._isMount = false;
}
timerJob()
{
if(this._isMount == false)
return;
// setState
}