Front-end Machine Coding Series #5: Building a Lightweight React Timer

Nitin SainiNitin Saini
2 min read

React gives you powerful tools like useState and useEffect to manage state and lifecycles. But sometimes you want to perform side effects or store values without triggering re-renders. That’s where useRef becomes incredibly handy.

In this article, we’ll build a smart counter using only useRef, complete with Start, Pause, and Reset controls, and it won’t cause a single re-render while counting. Guess what, it still one of the most asked interview problems that can give headache to anyone who doesn’t have any idea of using useRef.

Problem Statement

We want to build a performance-efficient counter component in React that:

  • Counts from 0 to 10 (given a time frame)

  • Avoid re-renders during count

  • Has proper controls:

    • Start: begin counting

    • Pause: stop temporarily without resetting

    • Reset: stop and reset to 0

React Code

import {useRef} from "react"

const Counter=()=>{
  //make three ref to store the states during re-renders
  const intervalRef=useRef(null);
  const displayRef=useRef(null);
  const counterRef=useRef(0);

  const startHandler=()=>{
    //setting the interval
      intervalRef.current=setInterval(()=>{
    //check if count exceeds 10
          if(counterRef.current>=10){
              clearInterval(intervalRef.current);
              return;
          }
    //incrementing the counter
        counterRef.current=counterRef.current+1;
        if(displayRef.current){ 
        displayRef.current.textContent=counterRef.current;
        }
      }, 1000)
  }

  const pauseHandler=()=>{
    clearInterval(intervalRef.current);
    intervalRef.current = null;
  }

  const resetHandler=()=>{
    clearInterval(intervalRef.current);
    if(displayRef.current){
      displayRef.current.textContent=0;
    }
    counterRef.current=null;
  }

  return(
    <div className="counter-container">
      <h2>React Counter</h2>
      <p className="counter-value"><span ref={displayRef}>0</span></p>
      <div className="button-group">
        <button onClick={startHandler}>Start</button>
        <button onClick={pauseHandler}>Pause</button>
        <button onClick={resetHandler}>Reset</button>
      </div>
    </div>
  )
}

export default Counter;

CSS code:

.counter-container {
  background-color: #1e1e2f;
  color: #e0e0e0;
  font-family: "Segoe UI", sans-serif;
  padding: 2rem;
  max-width: 400px;
  margin: 2rem auto;
  border-radius: 12px;
  text-align: center;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);
}

.counter-value {
  font-size: 3rem;
  font-weight: bold;
  margin: 1rem 0;
}

.button-group button {
  background-color: #2dd4bf;
  color: #1e1e2f;
  border: none;
  padding: 0.6rem 1.2rem;
  font-size: 1rem;
  font-weight: 500;
  margin: 0 0.4rem;
  border-radius: 8px;
  cursor: pointer;
  transition: all 0.2s ease-in-out;
}

.button-group button:hover {
  background-color: #14b8a6;
}

Wrap Up

Using useRef instead of useState in this counter:

  • Avoids unnecessary re-renders

  • Offers direct control over DOM and timers

  • Keeps your UI snappy and performance-optimized

This approach is great for non-UI state (timers, subscriptions, etc.), and it’s a good technique to have in your React toolbox.

Happy coding! If you have any questions or suggestions you'd like to explore further, feel free to drop a comment below.

See you in the next blog. Please don’t forget to follow me on:

Twitter
LinkedIn

0
Subscribe to my newsletter

Read articles from Nitin Saini directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Nitin Saini
Nitin Saini

A Full Stack Web Developer, possessing a strong command of React.js, Node.js, Express.js, MongoDB, and AWS, alongside Next.js, Redux, and modern JavaScript (ES6+)