Reputation: 29
What I have here is a simple timer app which might one day turn into a Pomodoro app. I'm learning about lifecycle methods right now.
Here's the problem. I'd like to remove the timer from the screen once it reaches 0. Eventually, it'll get instantly replaced by a different timer, but I haven't reached that part yet. I'm pretty sure the problem is here:
componentDidUpdate = () => {
if (this.state.count-1 === -2) {
this.props.timerOverCallback()
}
}
Originally, I tried this if statement: !(this.state.count-1). The counter kept clearing at 2 instead of 0.
Then I tried this.state.count-1 === -1 because that seemed to make sense still. The counter kept clearing at 1 instead of 0.
By then, a pattern had emerged and I tried this.state.count-1 === -2. My question is: why do I need to compare against -2 instead of -1? Comparing against -2 doesn't seem particularly reliable, really. So what's the best way to clear the timer at 0?
Here's the full code:
class Timer extends Component {
static propTypes = {
timerOverCallback: PropTypes.func.isRequired,
count: PropTypes.number.isRequired,
}
constructor(props) {
super(props)
this.state = {
timerOverCallback: this.props.timerOverCallback,
count: this.props.count,
}
}
decrease = () => {
this.setState(
prevState => ({count: prevState.count - 1})
)
}
componentDidMount = () => {
this.interval = setInterval(this.decrease, 1000)
}
componentDidUpdate = () => {
if (this.state.count-1 === -2) {
this.props.timerOverCallback()
}
}
componentWillUnmount = () => { clearInterval(this.interval) }
render() {
return (
<Text style={styles.count}>{this.state.count}</Text>
)
}
}
export default class App extends Component {
constructor() {
super()
this.state = {
timerOn: false,
}
}
toggleTimer = () => {
this.setState(
prevState => ({timerOn: !prevState.timerOn})
)
}
render() {
return (
<View
style={styles.fillAndCenter}>
{this.state.timerOn ?
<Timer
count={5}
timerOverCallback={this.toggleTimer}
/> : <View/>
}
<Button
title='Toggle Timer'
onPress={this.toggleTimer}
/>
<Text>
{this.state.timerOn ? 'On' : 'Off'}
</Text>
</View>
)
}
}
UPDATE 05/08/2018:
I took gaback's advice and placed...
if (this.state.count < 0) this.state.timerOverCallback()
... in this.decrease and got rid of componentDidMount. However, I realized that the component was in fact reaching the render stage of its lifecycle when this.state.count = -1, which seems problematic. It's true that the screen never actually updated to -1, but the render function was definitely getting called, so the fact that the screen wasn't updating seemed coincidental (right!?).
And then I realized that, even if I was being overly picky, there was simple way to fix that, too:
shouldComponentUpdate = (nextProps, nextState) => {
return nextState.count > -1
}
Upvotes: 0
Views: 549
Reputation: 638
This is componentDidUpdate
from React-Native site:
componentDidUpdate() is invoked immediately after updating occur
So let's say your screen showing 0
(this.state.count = 0)
. this.decrease
gets call => this.state.count = -1
=> Component gets update => componentDidUpdate
gets called ( before rendering so the screen still showing 0
)
and this.state.count - 1 = -2
, you call timeOverCallback()
and stop the screen from rendering the current this.state.count
which is -1
You could change to this.state.count === -1
or I think better way is stop using componentDidUpdate
, put the check in this.decrease
: when you see this.state.count < 0
calls timeOverCallback()
Upvotes: 1