≡ Menu

Creating a stopwatch is a rite of passage for React developers.

It’s the perfect project to master Hooks, Side Effects, and JavaScript Math.

In this post, we’ll break down a clean, functional implementation and explain the “why” behind the code.


The Core Logic

At its heart, a stopwatch is just a number (milliseconds) that increases at a set interval.

However, making that look like a “clock” requires some clever state management.

import React, { useState, useEffect, useRef } from 'react';

const Stopwatch = () => {
  const [isRunning, setIsRunning] = useState(false);
  const [time, setTime] = useState(0);
  const timerRef = useRef(null);
  
  // ... (logic below)

1. Managing the “Heartbeat” with useEffect

We use useEffect to start and stop the timer. The most important part here is the Cleanup Function.

Without it, if you delete the component or navigate away, the timer would keep running in the background, causing a “memory leak.”

useEffect(() => {
  if (isRunning) {
    // Increment time by 10ms every 10ms
    timerRef.current = setInterval(() => {
      setTime((prevTime) => prevTime + 10);
    }, 10);
  } else {
    clearInterval(timerRef.current);
  }

  return () => clearInterval(timerRef.current); // The Cleanup
}, [isRunning]);

2. The Math: Converting Milliseconds to Time

The time state is just a big number of milliseconds (e.g., 95000). To display this as 01:35:00, we use a formatting helper.

Key Concept: Modulo (%) > We use the modulo operator to “reset” the numbers. For example, seconds % 60 ensures that when we hit 61 seconds, the display shows 01 instead of 61.

const formatTime = (time) => {
  const minutes = Math.floor((time / 60000) % 60);
  const seconds = Math.floor((time / 1000) % 60);
  const milliseconds = Math.floor((time % 1000) / 10);

  return `${minutes.toString().padStart(2, '0')}:${seconds
    .toString()
    .padStart(2, '0')}:${milliseconds.toString().padStart(2, '0')}`;
};

3. Making it Look Professional with padStart

Ever noticed how digital clocks don’t “jump” when the numbers change?

We achieve this using .padStart(2, '0'). This ensures that if the seconds are 5, it displays as 05. This prevents the UI from “jittering” as the width of the numbers changes.


Summary of Performance

Because this stopwatch updates every 10ms, React is re-rendering the component 100 times per second.

For a simple UI, this is perfectly fine!

However, if you add complex animations or many child components, you might want to wrap the display in its own “leaf” component to keep the app snappy.

Key Takeaways

  • useRef is essential for storing the setInterval ID without causing extra re-renders.

  • Math.floor keeps our time units as whole integers.

  • String Concatenation (via Template Literals) turns raw math into a readable UI.

Useful links below:

Let me & my team build you a money making website/blog for your business https://bit.ly/tnrwebsite_service

Get Bluehost hosting for as little as $1.99/month (save 75%)…https://bit.ly/3C1fZd2

Best email marketing automation solution on the market! http://www.aweber.com/?373860

Build high converting sales funnels with a few simple clicks of your mouse! https://bit.ly/484YV29

Join my Patreon for one-on-one coaching and help with your coding…https://www.patreon.com/c/TyronneRatcliff

Buy me a coffee ☕️https://buymeacoffee.com/tyronneratcliff

 

 

{ 0 comments }

Master the JavaScript Fetch API: Beyond the Basics

javascript fetch api

If you’ve been writing JavaScript for a while, you’re likely familiar with the standard fetch('url') syntax.

It’s the modern standard for making network requests, replacing the clunky XMLHttpRequest.

But did you know that fetch() is actually designed to work with a set of dedicated interfaces? To write truly clean, modular, and reusable code, you should understand how to use the Request constructor.


The Anatomy of a Request

When you call fetch('https://api.example.com/data'), JavaScript implicitly creates a Request object for you. However, you can manually create this object using the new keyword. This is incredibly useful for defining your request logic in one place and executing it in another.

Using the new Request() Constructor

The Request() constructor takes two arguments:

  1. The URL: The resource you want to fetch.

  2. The Options (Optional): An object containing settings like method, headers, and body.

const myHeaders = new Headers();
myHeaders.append("Content-Type", "application/json");

const myRequest = new Request("https://api.example.com/posts", {
  method: "POST",
  headers: myHeaders,
  mode: "cors",
  cache: "default",
  body: JSON.stringify({ title: "New Post", body: "Hello World" }),
});

// Now, just pass the object to fetch
fetch(myRequest)
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('Error:', error));

Why use the Request object?

  • Reusability: You can define a base request and clone it using myRequest.clone() to make slight variations without re-typing headers.

  • Security: By separating the request definition from the execution, you can pass request objects into functions that handle the fetching logic without exposing sensitive URL logic.

  • Cleanliness: It keeps your fetch() calls readable. Instead of a 20-line configuration object inside the fetch call, you just pass a variable.


Handling the Response

Once the fetch is initiated, it returns a Promise that resolves to a Response object. This object doesn’t contain the data immediately; it’s a stream. You have to choose how you want to read it.

Method Description
.json() Parses the response body as JSON.
.text() Returns the response body as a raw string.
.blob() Used for images or files (Binary Large Objects).
.formData() Parses the body as form data.

Pro Tip: Error Handling

A common “gotcha” with fetch() is that it does not reject on HTTP error status codes (like 404 or 500).

The promise only rejects if the network request itself fails (e.g., DNS issues or loss of connection).

To catch 404s or 500s, you must check the ok property:

fetch(myRequest)
  .then(response => {
    if (!response.ok) {
      throw new Error(`HTTP error! Status: ${response.status}`);
    }
    return response.json();
  })
  .then(data => console.log(data))
  .catch(e => console.log("There was a problem: " + e.message));

Summary

The Fetch API is more than just a function; it’s a powerful suite of tools.

By using new Request(), you gain better control over your network layer, making your code more professional and easier to maintain.

Useful links below:

Let me & my team build you a money making website/blog for your business https://bit.ly/tnrwebsite_service

Get Bluehost hosting for as little as $1.99/month (save 75%)…https://bit.ly/3C1fZd2

Best email marketing automation solution on the market! http://www.aweber.com/?373860

Build high converting sales funnels with a few simple clicks of your mouse! https://bit.ly/484YV29

Join my Patreon for one-on-one coaching and help with your coding…https://www.patreon.com/c/TyronneRatcliff

Buy me a coffee ☕️https://buymeacoffee.com/tyronneratcliff

{ 0 comments }