Countdown timer in react-native

44,839

Solution 1

In your code setTimeout is called in componentDidMount and ComponetDidMount will be called once in whole component lifeCycle. So, the function within setTimeout will be called once only. i.e. just after the first render but upon successive render, the componentDidMount won't be called.

Solution to your problem can be:

constructor(props: Object) {
  super(props);
  this.state ={ timer: 3}
}

componentDidMount(){
  this.interval = setInterval(
    () => this.setState((prevState)=> ({ timer: prevState.timer - 1 })),
    1000
  );
}

componentDidUpdate(){
  if(this.state.timer === 1){ 
    clearInterval(this.interval);
  }
}

componentWillUnmount(){
 clearInterval(this.interval);
}

render() {
  return (
    <View style={{ flex: 1, justifyContent: 'center', }}>
      <Text> {this.state.timer} </Text>
    </View> 
  )
}

'setInterval' vs 'setTimeout'

Advantage of using a function in setState instead of an object

memory leak because of setInterval: if we unmount the component before clearInterval called, there is a memory leak because the interval that is set when we start and the timer is not stopped. React provides the componentWillUnmount lifecycle method as an opportunity to clear anything that needs to be cleared when the component is unmounted or removed.

I have created a custom hook called useCountDownTimer to use this functionality in a functional component. If interested please check this GitHub gist link

Solution 2

Updated Hooks (using useEffect) version to countdown using setInterval in react-native:

const [timerCount, setTimer] = useState(60)

useEffect(() => {
  let interval = setInterval(() => {
    setTimer(lastTimerCount => {
        lastTimerCount <= 1 && clearInterval(interval)
        return lastTimerCount - 1
    })
  }, 1000) //each count lasts for a second
  //cleanup the interval on complete
  return () => clearInterval(interval)
}, []);

use the state variable timerCount as: <Text>{timerCount}</Text>

Solution 3

The hooks version.

function CountDown() {
   const [count, setCount] = useState(3)

   useEffect(() => 
     let interval = setInterVal(() => {
        setCount(prev => {
           if(prev === 1) clearInterval(interval)
           return prev - 1
        })
     })
     // interval cleanup on component unmount
     return () => clearInterval(interval)
   ), [])

   return <Text>{count}</Text>
}

Solution 4

If anyone wants to start the timer again on a button press, this will be the code in react-hooks:

let timer = () => {};

const myTimer = () => {
    const [timeLeft, setTimeLeft] = useState(30);

    const startTimer = () => {
        timer = setTimeout(() => {
            if(timeLeft <= 0){
                clearTimeout(timer);
                return false;
            }
         setTimeLeft(timeLeft-1);
        }, 1000)
     }

     useEffect(() => {
         startTimer();
         return () => clearTimeout(timer);
     });    

    const start = () => {
        setTimeLeft(30);
        clearTimeout(timer);
        startTimer();
    }

    return (
       <View style={styles.container}>
          <Text style={styles.timer}>{timeLeft}</Text>
          <Button onPress={start} title="Press"/>
    </View>
)}

Here in this example, I have taken a timer of 30 seconds

Share:
44,839

Related videos on Youtube

Shashika
Author by

Shashika

Updated on July 09, 2022

Comments

  • Shashika
    Shashika almost 2 years

    I want to countdown from 3 to 1 when a screen is loaded in react-native. I tried it with setTimeOut like this and it didn't work. What am I doing wrong here? How can I achieve this? When the screen is loaded, I want to show 3 =-> 2 ==> 1 with 1 second interval. Here is my code.

     constructor(props) {
            super(props);
    
            this.state = {
                timer: 3
            }
        }
    
        // componentDidMount 
        componentDidMount() {
            setTimeout(() => {
                this.setState({
                    timer: --this.state.timer
                })
            }, 1000);
        }
    
  • fv_dev
    fv_dev over 5 years
    If you're going to update state with current state you're better off using : this.setState((prevState) => { return { timer: prevState.timer-- } })
  • Mohammed Ashfaq
    Mohammed Ashfaq over 5 years
    @fv_dev is there any advantage in using this method.
  • Joshua Pinter
    Joshua Pinter almost 5 years
    @MohammedAshfaq They say that the current state cannot be reliably gotten with this.state whereas using prevState it is. I have yet to be convinced with an example.
  • Tee
    Tee over 2 years
    can you explain what lastTimerCount <= 1 && clearInterval(interval) is doing?
  • alexanderdavide
    alexanderdavide over 2 years
    @Tee This clears the interval once it remains under 1 second and has finished so to say. clearInterval is an inbuilt function in the browser window or NodeJS to clear the interval so that it isn't executed any longer. Similarly, there is clearTimeout for setTimeout.
  • Tee
    Tee over 2 years
    @alexanderdavide Thanks for the response. Intuitively, I figured that's what was happening. Just the syntax of the expression is a little unexpected -- I was looking for some logic like if (lastTimerCount <= 1) {clearInterval(interval)} ... The && between a conditional expression and a method is the part I need to wrap my head around.

Related