Search code examples
javascriptreactjsecmascript-6timer

How to reset the timer component upon completion


I am trying to build a timer component using the react-countdown-now package: https://www.npmjs.com/package/react-countdown-now#key.

I was having trouble to reset the timer so that it moves on to the next time on the schedule.

I have been trying to use key property in props it pass it in an array of times to wait till (it was in the documentation). In reality I would get these values of a schedule from a server side method.

Currently I have

Component:

<Countdown
     date={Date.now() + 5000}
     key = {timeDelays}
     intervalDelay={0}
     precision={3}
     renderer={timerRenderer}
/>

Supporting functions and Values:

//These time values are most probably going to be in JSON format, 
//and probably will contain EPOCH times for scheduled events

const timeDelays = [2000,4000,3000,15789,2345794];

// Random component
const Completionist = () => <span>You are good to go!</span>;

// Renderer callback with condition
const timerRenderer = ({ hours, minutes, seconds, completed }) => {
     // if (completed) {
        //     Render a completed state
        //     return <Completionist />;
     // } else {
     //     // Render a countdown
            return <span>{hours}:{minutes}:{seconds}</span>;
     //}
};

I want it to start with a countdown from the list and then when completed move onto the next schedule value from the list.


Solution

  • This is a total change from the former answer, which used a class-based component.

    First, we'll need to import React and React hooks to our component file.

    import React, { useState } from 'react';
    

    Next, we'll declare a React function component and use React hooks to maintain state.

    function MyCountdownTimer({ times }) {
        // a hook for the current time index
        const [currentTimeIndex, setCurrentTimeIndex] = useState(0);
        // a hook for the current time
        const [currentTime, setCurrentTime] = useState(null);
        // return a render
        return (
            <Countdown
                date={currentTime}
                key={currentTimeIndex}
                onComplete={() => {
                    // dont's move to next time if just done with last time
                    if(times.length - 1 <= times.indexOf(currentTime)) return;
                    // move to next time index
                    setCurrentTimeIndex(currentTimeIndex + 1);
                    // reset current time
                    setCurrentTime(new Date(times[currentTimeIndex + 1]));
                }}
                renderer={({ hours, minutes, seconds, completed }) => {
                    // render completed
                    if (completed) return <span>You are good to go!</span>;
                    // render current countdown time
                    return <span>{hours}:{minutes}:{seconds}</span>;
    
                }}
            />
        );
    }
    

    An implementation of this would look something like so.

    let times = [...] // an array of times
    
    <MyCountdownTimer times={times} />
    

    React hooks are still a bit new so for a better understanding on React Hooks you can follow this link https://reactjs.org/docs/hooks-intro.html.

    NOTE

    1. You need a way to tell what time you're currently on so within your component you'll have two things. The list of times(times) as an array this should be passed as a prop as suggested in the code above, the index of the current time(currentTimeIndex) as an integer and the current time(currentTime) as a Date object.

    2. You'll need to listen for when the timer hits zero using the onComplete property to define a callback method, we do not update the component's state when the Countdown timer has been completed.

    3. A key property on the Countdown component, this is meant to change for each time you want to reset the countdown timer and since we're incrementing the index to go to the next time we'll simply use the index of the current time.

    4. I reduced the code of your renderer so you can render what you need to within the same function, except if you will be adding a lot more code in there than that.

    5. This is using a function component with hooks to maintain state.

    6. The date according to the documentation can be a date object.