Stopwatch
⏲️

Stopwatch

Tags
React.js
styled-components
TypeScript

Background:

Building a stopwatch app is a simple project that gets asked in front-end web interviews. I personally have needed to build one when interviewing at two different companies in the past. When I originally wrote my stopwatch app I used a class based approach, leveraging the different lifecycle functions of react class components to update the timer. I wanted to attempt this again with React hooks and see if I could simplify my approach and reduce the amount of code needed.

Objective:

Build a stopwatch app that can start, stop, and reset a timer using React function components.

Managing the Timer:

I leveraged useEffect for handling the work needed to update the value of the timer. isRunning is a state value I created that toggles to on via a callback on the start button’s onClick handler function.
const handleStartButton = () => setIsRunning(true);
In the above snippet, the state value, isRunning is updated to true when the callback function is executed.
I wanted to ensure that no race condition occurred when updating the time value using setTime, so I passed the params to the setter as a callback function, pulling the current state time value from the params for incrementing. I could use time and handle incrementing without a callback, like so
setTime(time + 1)
however, this would require adding time to the array passed to the second argument of the useEffect function. This was less desirable because the useEffect would execute on every update to time (every 10 ms!). Leaving time out of the equation and instead pulling the current value from the callback function was more efficient and only executes the useEffect when the user clicked a button.
useEffect(() => { const timer: NodeJS.Timer = setInterval( (): void => setTime((prevTime) => prevTime + 1), 10 ); if (!isRunning) { clearInterval(timer); } return () => clearInterval(timer); }, [isRunning, setInterval]);
The above snippet is my solution. The interval is cleared when isRunning returns to false (when the stop button is clicked) or if the component unmounts. It is important to always handle the unmount to avoid any memory leaks.

Styling the App:

I used styled-components for styling the time and buttons. I really enjoy using CSS-in-JS to style and read React component code. I am able to organize buttons as <StartButton> and <StopButton> which I find useful for quickly finding these components in the code.
A benefit of using styled-components is the dynamic creation of classes and injection into the head of the DOM. This fact is less important for this app because of the simple design and not a lot of components being added and removed.

Future Expansion:

Laps -
Laps are a way to find the difference in time between a specific interval. For example, if a marathon runner wanted to know how fast they ran each mile, they could either reset the timer after each mile or note the time at each mile. Both ideas are undesirable because the total time is reset. A lap time would record the difference of the total time from the last lap to give the runner the time of the previous mile.
Offline Mode -
The app currently resets when the app window is closed or the page is refreshed. This is because the state is not persisted. Using local storage or an API would help with preserving the last recorded time before the app unmounted. There would need to be additional functions on app load to find the difference in time between the last occurrence that time was set, the value of time, and the current time.