Reputation: 3
I'm coding a simple pomodoro timer which is fired using a click of the button.
tick() {
if (this.state.minutes > 0) {
setInterval(() => {
this.timer(this.state.minutes * 60 + this.state.seconds - 1);
}, 1000);
}
}
timer(x) {
this.setState(
{
minutes: Math.trunc(x),
seconds: (x*60) % 60
},
() => this.tick()
);
}
render() {
return (
<div className="App">
<div className="Pomodo">
<div className="counter">
{this.state.minutes + ":" + this.state.seconds}
</div>
<button className="btnCode" onClick={() => this.timer(25)}>
Code
</button>
<button className="btnCoffee" onClick={() => this.timer(5)}>
Coffee
</button>
</div>
</div>
);
}
}
export default App;
This shows the timer as follow:
25:00 (correct)
24:59 (correct)
24:57 == wrong should be 58
24:53 == wrong should be 57
...etc
What am I missing here please? When troubleshooting via chrome the counter is fine and shows the correct numbers.
Upvotes: 0
Views: 41
Reputation: 290
It is skipping the seconds because, everytime you call tick()
, you are setting a new interval by calling this.tick()
in setState
. You can fix this by adding a flag after calling tick for the first time.
One more issue I see is, in the btnCode's onClick
, you are passing 25
as minutes to this.timer
. But, in setInterval, you are calling timer with everything seconds. I suggest you pass everything in seconds to timer function. Also, it is good to clear intervals at unmounting.
See the adjusted code.
class TodoApp extends React.Component {
constructor(props) {
super(props)
this.state = {
minutes: 0,
seconds: 0,
started: false
}
this.tick = this.tick.bind(this);
this.timer = this.timer.bind(this);
}
tick() {
// Add flag so that tick is not called again
this.setState({
started: true
})
if (this.state.minutes > 0) {
this.interval = setInterval(() => {
const seconds = this.state.minutes * 60 + this.state.seconds - 1;
this.timer(seconds);
}, 1000);
}
}
componentWillUnmount() {
clearInterval(this.interval);
}
timer(x) {
this.setState(
{
minutes: Math.trunc(x / 60),
seconds: x % 60
},
() => !this.state.started && this.tick() // only call if its timer is not started before
);
}
render() {
return (
<div className="App">
<div className="Pomodo">
<div className="counter">
{this.state.minutes + ":" + this.state.seconds}
</div>
<button className="btnCode" onClick={() => this.timer(25 * 60)}>
Code
</button>
<button className="btnCoffee" onClick={() => this.timer(5 * 60)}>
Coffee
</button>
</div>
</div>
);
}
}
ReactDOM.render(<TodoApp />, document.querySelector("#app"))
<div id="app"></div>
<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>
Upvotes: 1