Build a useTimer Countdown Hook
Many modern applications leverage dynamic countdowns for features like OTP expiration, session timeouts, or game mechanics. Building a reusable timer hook encapsulates this logic, making it easy to integrate robust timer functionality into any component. This challenge focuses on creating such a hook that provides essential control over the timer's lifecycle.
Problem Description
You are tasked with developing a language-agnostic custom hook, provisionally named useTimer, that manages a countdown timer. This hook should initialize with a specified number of seconds and expose its current time remaining along with functions to control its operation.
Key requirements:
- Initialization: The hook should accept an
initialSecondsparameter. Upon initialization,timeLeftshould be set to thisinitialSeconds. timeLeft: This value should represent the current number of seconds remaining. It should be updated every second when the timer is active. OncetimeLeftreaches 0, it should stop decrementing.start():- If the timer is paused or stopped (and
timeLeft> 0), callingstart()should begin/resume the countdown from the currenttimeLeft. - If the timer is already running, calling
start()should have no additional effect. - If
timeLeftis already 0, callingstart()should have no effect untilreset()is called.
- If the timer is paused or stopped (and
pause():- If the timer is running, calling
pause()should halt the countdown, preserving the currenttimeLeft. - If the timer is already paused or stopped, calling
pause()should have no additional effect.
- If the timer is running, calling
reset():- Calling
reset()should immediately stop any active countdown. - It should then reset
timeLeftback to theinitialSecondsvalue provided during the hook's creation. - The timer should be in a paused state after
reset().
- Calling
Expected Behavior:
- The timer should decrement
timeLeftby 1 every second. - The timer should automatically stop when
timeLeftreaches 0. - Subsequent calls to
start()aftertimeLeftis 0 should not restart the timer without a precedingreset().
Edge Cases:
- What happens if
initialSecondsis 0? The timer should immediately show 0 andstart()should not decrement further. - What happens if
start()is called multiple times withoutpause()? It should not create multiple intervals. - What happens if
pause()is called when the timer is already paused or not running? It should have no effect. - What happens if
reset()is called while the timer is running? It should stop and reset correctly.
Examples
Example 1: Basic Countdown Lifecycle
Input:
initialSeconds = 5
Operation Sequence:
1. Initialize useTimer(5)
2. Call start()
3. Wait 2 seconds
4. Call pause()
5. Wait 1 second
6. Call start()
7. Wait 3 seconds
Output:
After init: [timeLeft: 5, start(), pause(), reset()]
After start(): Timer begins decrementing.
After 2s (before pause): [timeLeft: 3, start(), pause(), reset()]
After pause(): Timer stops.
After 1s (during pause): [timeLeft: 3, start(), pause(), reset()]
After start() again: Timer resumes decrementing.
After 3s (timer finishes): [timeLeft: 0, start(), pause(), reset()] (Timer automatically stops at 0)
Explanation: The timer correctly counts down, pauses, and resumes. It automatically stops when timeLeft reaches 0.
Example 2: Reset and Zero Initial Seconds
Input:
initialSeconds = 3
Operation Sequence:
1. Initialize useTimer(3)
2. Call start()
3. Wait 1 second
4. Call reset()
5. Call start()
6. Wait 4 seconds
7. Initialize useTimer(0)
8. Call start()
Output:
After init useTimer(3): [timeLeft: 3, start(), pause(), reset()]
After start() and 1s: [timeLeft: 2, start(), pause(), reset()]
After reset(): [timeLeft: 3, start(), pause(), reset()] (Timer is paused)
After start() and 4s: [timeLeft: 0, start(), pause(), reset()] (Timer counts down 3->2->1->0 and stops)
After init useTimer(0): [timeLeft: 0, start(), pause(), reset()]
After start() for useTimer(0): [timeLeft: 0, start(), pause(), reset()] (No change, as initial is 0)
Explanation: The timer correctly resets to its initial state, even when running. A timer initialized with 0 seconds correctly starts and stays at 0, and subsequent start() calls have no effect.
Constraints
initialSecondswill be a non-negative integer. (0 <=initialSeconds)- The timer should update approximately once every second. Precision is important but minor clock drift is acceptable.
- The hook should be designed for a single instance of a timer; it doesn't need to support multiple independent timers from a single hook call (though multiple instances of the hook can be used in a larger application).
- No specific UI framework is required; focus on the core logic and state management.
Notes
- Consider how to manage the interval (e.g.,
setIntervalor an equivalent time-based mechanism) and ensure proper cleanup to prevent memory leaks, especially when the hook or its containing component might "unmount" or be disposed of. - Think about the internal state variables needed to track the
timeLeft, whether the timer is currently running, and the originalinitialSecondsvalue. - The solution should prioritize clear state transitions and robust handling of the control functions (
start,pause,reset) in all scenarios.