Hone logo
Hone
Problems

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:

  1. Initialization: The hook should accept an initialSeconds parameter. Upon initialization, timeLeft should be set to this initialSeconds.
  2. timeLeft: This value should represent the current number of seconds remaining. It should be updated every second when the timer is active. Once timeLeft reaches 0, it should stop decrementing.
  3. start():
    • If the timer is paused or stopped (and timeLeft > 0), calling start() should begin/resume the countdown from the current timeLeft.
    • If the timer is already running, calling start() should have no additional effect.
    • If timeLeft is already 0, calling start() should have no effect until reset() is called.
  4. pause():
    • If the timer is running, calling pause() should halt the countdown, preserving the current timeLeft.
    • If the timer is already paused or stopped, calling pause() should have no additional effect.
  5. reset():
    • Calling reset() should immediately stop any active countdown.
    • It should then reset timeLeft back to the initialSeconds value provided during the hook's creation.
    • The timer should be in a paused state after reset().

Expected Behavior:

  • The timer should decrement timeLeft by 1 every second.
  • The timer should automatically stop when timeLeft reaches 0.
  • Subsequent calls to start() after timeLeft is 0 should not restart the timer without a preceding reset().

Edge Cases:

  • What happens if initialSeconds is 0? The timer should immediately show 0 and start() should not decrement further.
  • What happens if start() is called multiple times without pause()? 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

  • initialSeconds will 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., setInterval or 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 original initialSeconds value.
  • The solution should prioritize clear state transitions and robust handling of the control functions (start, pause, reset) in all scenarios.
Loading editor...
plaintext