AniUI Academy
medium+200 XP

Build a countdown timer

Build a 5-second countdown with Start/Pause and Reset controls.

Pausing has to actually stop the countdown, and resuming has to continue from where it left off — not restart, and not keep ticking in the background while "paused".

Requirements

  • A `data-testid="time-left"` element shows the remaining whole seconds, starting at 5.
  • A `data-testid="start-pause"` button reads "Start" while stopped and "Pause" while running, and toggles between the two states.
  • While running, the count decreases by 1 every 1000ms.
  • While paused, the count does not change at all, however long you wait.
  • A `data-testid="reset"` button returns the count to 5 and stops the timer.

Your workspace

import { useEffect, useState } from "react";

const START_SECONDS = 5;

export default function App() {
  const [secondsLeft, setSecondsLeft] = useState(START_SECONDS);
  const [running, setRunning] = useState(false);

  useEffect(() => {
    // TODO: this interval should only tick while `running` is true — right
    // now it counts down regardless of whether the timer is "paused".
    const id = setInterval(() => {
      setSecondsLeft((s) => Math.max(0, s - 1));
    }, 1000);
    return () => clearInterval(id);
  }, []);

  return (
    <div style={{ fontFamily: "sans-serif", padding: 16 }}>
      <p style={{ fontSize: 32 }}>
        <span data-testid="time-left">{secondsLeft}</span>s
      </p>
      <button data-testid="start-pause" onClick={() => setRunning((r) => !r)}>
        {running ? "Pause" : "Start"}
      </button>
      <button
        data-testid="reset"
        onClick={() => {
          setSecondsLeft(START_SECONDS);
          setRunning(false);
        }}
        style={{ marginLeft: 8 }}
      >
        Reset
      </button>
    </div>
  );
}

Ready to check it?

5 tests run against your rendered component, right here in your browser. Sign in to claim the XP when you pass.

Stuck? The react course covers the ideas this problem is built on.