Reputation: 13
I'm trying to implement a simple timer component in React that starts running on props update. Everything works but the timer. • Props are being received and processed properly. • Initial "seconds" property of the state is being defined in the constructor.
But this.state is undefined inside this.setState being called from componentDidMount with the asynchronous method setInterval to run the timer.
I also tried binding the startTimer method to this in the constructor but it throws the same error. this.startTimer = this.startTimer.bind(this);
Here is the component & thank you in advance:
import React, { Component } from 'react';
import { connect } from "react-redux";
class Timer extends Component {
constructor(props) {
super(props);
this.state = { seconds: 0 }
this.intervalHandle;
}
//Component receives this.props.started === true properly
componentDidMount() {
if (this.props.started) {
this.startTimer();
}
}
//startTimer triggers correctly logging "STARTING"
startTimer() {
console.log("STARTING")
this.intervalHandle = setInterval(
this.tick, 1000);
}
//tick also triggers correctly logging "Ticking" every 1000ms
tick() {
console.log("TICKING")
//HERE IS THE ERROR: Uncaught TypeError: Cannot read property
//'seconds' of undefined, the console throws it once a seconds
this.setState({ seconds: this.state.seconds + 1 })
}
componentWillUnmount() {
if (!this.props.started) {
clearInterval(this.intervalHandle);
}
}
// Component initialy renders
render() {
return <span>:0{this.state.seconds}</span>
}
}
function mapStateToProps(state) {
return {
started: state.isStarted
}
}
export default connect(mapStateToProps)(Timer);
Upvotes: 0
Views: 815
Reputation: 34004
The reason it is undefined because this.tick is a function and you are not binding it.
Change it to
this.intervalHandle = setInterval(this.tick.bind(this), 1000);
or bind it in constructor
constructor(props){
super(props);
this.tick = this.tick.bind(this);
}
this.intervalHandle = setInterval(this.tick, 1000);
or use arrow function
this.intervalHandle = setInterval(() => this.tick(), 1000);
tick = () => {
console.log("TICKING")
this.setState({ seconds: this.state.seconds + 1 })
}
Both the changes should resolve your issue
Upvotes: 2