Rachel
Rachel

Reputation: 61

How to run a series of Count Down Clock

I am trying to run multiple count-down from an array, e.g., [11, 12, 13, 14, 15, 16]. What I would like to achieve is that the first time set count down time to 11, when it reach 0, the timer is set to 12 and then count down to 0. After that, reset to 13 and count down, then reset to 14 and count down, etc.

However, my code can only count down from 11 to 0, and then stop. For loop seems not working and never put the second item 12 into the timer. I later found out it is because the retun inside for loop break it outside of the loop. I wonder if there are some smart ways to avoid return in a For-Loop? Or, how to use Return in For-Loop without breaking out of the loop?

(I have another counter there called TotalTime which I inteneded to count all the total time it takes, ie, 11+12+13+14,etc)

The Timer and Display Screen:

import {View, Text, StyleSheet, FlatList} from 'react-native';
import PlaySounds from './PlaySounds';

const CustomTimer = ({time, length}) => {
  const [seconds, setSeconds] = useState(time);
  const [totalTime, setTotalTime] = useState(0);
      
  useEffect(() => {   
    if (seconds > 0 ) 
       { const interval = setInterval(() => {
          setSeconds(seconds => seconds - 1);
          setTotalTime(seconds=>seconds + 1)
        }, 1000);
        return () => clearInterval(interval);
      }}, [seconds])
     
    return (
       <View>
         <Text style={{fontSize:20}}> Count Down: {seconds} sec</Text>
         <Text style={{fontSize:20}}> Total Time: {totalTime} sec</Text>

       </View>

    )}

export default CustomTimer;


=====================
import React, {useEffect, useState} from 'react';
import {SafeAreaView,View,Button,ScrollView, Text, StyleSheet} from 'react-native';
import CustomTimer from '../component/CustomTimer';

const BrewScreen = () => {
    const timeArray= [11, 12, 13, 14, 15, 16]
    const length = timeArray.length
   
    const countDownArray=() =>{
    for (let i=0; i<length; i++) {
      return(<CustomTimer time={timeArray[i]} length={length}/>)
      }
    }
   
   
    return (
        <>
        <ScrollView>
          {countDownArray()}
        </ScrollView>
        </>
    )
}

Upvotes: 0

Views: 372

Answers (2)

Rachel
Rachel

Reputation: 61

I finally solve it, not using loops but the all mighty useEffect:

const CustomTimer = ({time}) => {
  
  
  const [seconds, setSeconds] = useState(time[0]);
  const [count, setCount] = useState(1)
  const [totalTime, setTotalTime] = useState(0);
  
  useEffect(() => {
    if (seconds >= 0 ) 
       { const interval = setInterval(() => {
         setSeconds(seconds => seconds - 1);
         setTotalTime(seconds=> seconds + 1)
         console.log('seconds ', seconds, ' totalTime ', totalTime)
        }, 1000);
        return () => clearInterval(interval);}
      else if (count<(time.length)) { 
        setCount(count => count+1)
        setSeconds(time[count])}
      else return
    } 
      )
  
    return (
       <View>
         <Text style={{fontSize:20}}> Count Down: {seconds} sec</Text>
         <Text style={{fontSize:20}}> Total Time: {totalTime} sec</Text>
         {seconds===3?<PlaySounds/>:null}
       </View>

    )}
  1. Having Return inside For Loop will break out of the loop
  2. JS will execute all the i in one go so you need to add Promise with Async/Await pair
  3. Return won't break ForEach loop
  4. However, Async does not work in ForEach loop
  5. useEffect automatically run the loop with proper support of time Interval
  6. In my case where I want to run a count-down clock, setInterval is must preferred to setCountDown, since setInterval renew EVERY milisecond you specified.
  7. Remeber to clean up your interval with clearInterval
  8. There is one thing I don't understand: The initial value of count has to be 1 instead of 0. It seems like count is forced into initial value the first time it is read. If I initialized it as 0, the first number in array will be executed twice.
  9. Also, the final number displayed in Count Down is -1.... I know it is because this is how the useEffect is stopped, but wonder how to avoid showing a negative number?

Upvotes: 1

Niklas Burggraaff
Niklas Burggraaff

Reputation: 121

The issue seems to be that you return inside the for loop.

This means that during the first iteration of the for loop in the countDownArray function you return the Timer element. When you do this, the function will exit and so the loop will not continue.

What you would need instead to achieve your desired behaviour is a different way of creating the Timer elements. This will likely require a callback function in the Timer element. This can be used to update the state of the BrewScreen, and update the Timer displayed.

Upvotes: 1

Related Questions