useStopwatch
React hook to create a stopwatch functionality.
Add the hook via the CLI:
npx @novajslabs/cli add useStopwatch
npx @novajslabs/cli add useStopwatch
pnpm dlx @novajslabs/cli add useStopwatch
Or copy and paste the code into your project:
import { useState, useEffect } from "react";
const addLeadingZero = (digit: number): string => {
let timeStr = "";
digit % 10 === digit ? (timeStr += `0${digit}`) : (timeStr += `${digit}`);
return timeStr;
};
interface Stopwatch {
current: string;
isPaused: boolean;
isOver: boolean;
currentDays: number;
currentHours: number;
currentMinutes: number;
currentSeconds: number;
elapsedSeconds: number;
pause: () => void;
play: () => void;
reset: () => void;
togglePause: () => void;
}
export const useStopwatch = (): Stopwatch => {
const [time, setTime] = useState({
days: 0,
hours: 0,
minutes: 0,
seconds: 0,
});
const [paused, setPaused] = useState(false);
const divider = ":";
const [isOver, setIsOver] = useState(false);
useEffect(() => {
if (paused) {
return;
}
const interval = setInterval(() => {
setTime((prev) => {
let d = prev.days;
let h = prev.hours;
let m = prev.minutes;
let s = prev.seconds;
if (s + 1 >= 60) {
s = 0;
if (m + 1 >= 60) {
m = 0;
if (h + 1 >= 24) {
h = 0;
d++;
} else {
h++;
}
} else {
m++;
}
} else {
s++;
}
return { days: d, hours: h, minutes: m, seconds: s };
});
}, 1000);
return () => clearInterval(interval);
}, [time, paused]);
return {
current: `${addLeadingZero(time.days)}${divider}${addLeadingZero(
time.hours
)}${divider}${addLeadingZero(time.minutes)}${divider}${addLeadingZero(
time.seconds
)}`,
isPaused: paused,
isOver,
currentDays: time.days,
currentHours: time.hours,
currentMinutes: time.minutes,
currentSeconds: time.seconds,
elapsedSeconds:
time.days * 86400 + time.hours * 3600 + time.minutes * 60 + time.seconds,
pause: () => setPaused(true),
play: () => setPaused(false),
reset: () => {
setIsOver(false);
setTime({ days: 0, hours: 0, minutes: 0, seconds: 0 });
},
togglePause: () => {
setPaused(!paused);
},
};
};
import { useState, useEffect } from "react";
const addLeadingZero = (digit) => {
let timeStr = "";
digit % 10 === digit ? (timeStr += `0${digit}`) : (timeStr += `${digit}`);
return timeStr;
};
export const useStopwatch = () => {
const [time, setTime] = useState({
days: 0,
hours: 0,
minutes: 0,
seconds: 0,
});
const [paused, setPaused] = useState(false);
const divider = ":";
const [isOver, setIsOver] = useState(false);
useEffect(() => {
if (paused) {
return;
}
const interval = setInterval(() => {
setTime((prev) => {
let d = prev.days;
let h = prev.hours;
let m = prev.minutes;
let s = prev.seconds;
if (s + 1 >= 60) {
s = 0;
if (m + 1 >= 60) {
m = 0;
if (h + 1 >= 24) {
h = 0;
d++;
} else {
h++;
}
} else {
m++;
}
} else {
s++;
}
return { days: d, hours: h, minutes: m, seconds: s };
});
}, 1000);
return () => clearInterval(interval);
}, [time, paused]);
return {
current: `${addLeadingZero(time.days)}${divider}${addLeadingZero(
time.hours
)}${divider}${addLeadingZero(time.minutes)}${divider}${addLeadingZero(
time.seconds
)}`,
isPaused: paused,
isOver,
currentDays: time.days,
currentHours: time.hours,
currentMinutes: time.minutes,
currentSeconds: time.seconds,
elapsedSeconds:
time.days * 86400 + time.hours * 3600 + time.minutes * 60 + time.seconds,
pause: () => setPaused(true),
play: () => setPaused(false),
reset: () => {
setIsOver(false);
setTime({ days: 0, hours: 0, minutes: 0, seconds: 0 });
},
togglePause: () => {
setPaused(!paused);
},
};
};
Requirements
React 16.8 or higher
Return values
current
Type: string
The current value of the stopwatch.
isPaused
Type: boolean
Represents whether the stopwatch is paused (true
) or not (false
).
isOver
Type: boolean
Represents whether the stopwatch has ended (true
) or not (false
).
currentDays
Type: number
The current value of days on the stopwatch.
currentHours
Type: number
The current value of hours on the stopwatch.
currentMinutes
Type: number
The current value of minutes on the stopwatch.
currentSeconds
Type: number
The current value of the seconds in the stopwatch.
elapsedSeconds
Type: number
The number of seconds that have passed since the start of the stopwatch.
pause
Type: function
Pause the stopwatch.
play
Type: function
Play the stopwatch.
reset
Type: function
Reset the stopwatch.
togglePause
Type: function
Toggle between pausing and playing the stopwatch.
Example
import { useStopwatch } from "./hooks/useStopwatch";
const App = () => {
const {
current,
isPaused,
isOver,
currentDays,
currentHours,
currentMinutes,
currentSeconds,
elapsedSeconds,
pause,
play,
reset,
togglePause,
} = useStopwatch();
return (
<div>
<p>Counter value: {current}</p>
<p>Is the counter paused? {isPaused ? "Yes" : "No"}</p>
<p>Has the counter over? {isOver ? "Yes" : "No"}</p>
<p>Current days: {currentDays}</p>
<p>Current hours: {currentHours}</p>
<p>Current minutes: {currentMinutes}</p>
<p>Current seconds: {currentSeconds}</p>
<p>Elapsed seconds: {elapsedSeconds}</p>
<button onClick={pause}>Pause</button>
<button onClick={play}>Play</button>
<button onClick={reset}>Reset</button>
<button onClick={togglePause}>Toggle Pause</button>
</div>
);
};
export default App;
Use cases
Here are some use cases where this React hook is useful:
- Tracking time elapsed during a coding session in a developer productivity tool
- Creating a stopwatch feature for tracking workout durations in a fitness tracking app