Jake Worth

Jake Worth

How to Create a Simple Timer in React with setInterval

Published: December 29, 2022 4 min read

  • react

Have you ever wanted to create a timer in a React app? In this post, I’ll explain how to create a timer effect in a React application using hooks and setInterval.

The Code

Let’s get right to the code. Below, I’ll explain what every line is doing.

import React, { useState } from "react";

const App = () => {
  const [timer, setTimer] = useState(undefined);

  const toggleTimer = () => {
    if (timer) {
      clearInterval(timer);
      setTimer(undefined);
    } else {
      const timerId = setInterval(() => console.log("tick"), 1000);
      setTimer(timerId);
    }
  };

  return <button onClick={toggleTimer}>{timer ? "Stop" : "Start"}</button>;
};

export default App;

What’s Happening?

Here’s the summary: this code creates a button that reads “Start”. On click, that changes to “Stop”, creating a timer with setInterval that ticks every second. This tick behavior could represent anything, including:

Let’s start reading at return. There, we have a button that shows a stateful call to action.

import React, { useState } from "react";

const App = () => {
  const [timer, setTimer] = useState(undefined);

  const toggleTimer = () => {
    if (timer) {
      clearInterval(timer);
      setTimer(undefined);
    } else {
      const timerId = setInterval(() => console.log("tick"), 1000);
      setTimer(timerId);
    }
  };

  return <button onClick={toggleTimer}>{timer ? "Stop" : "Start"}</button>;};

export default App;

Okay, what’s that state? It’s a timer, and it’s initially undefined:

import React, { useState } from "react";

const App = () => {
  const [timer, setTimer] = useState(undefined);
  const toggleTimer = () => {
    if (timer) {
      clearInterval(timer);
      setTimer(undefined);
    } else {
      const timerId = setInterval(() => console.log("tick"), 1000);
      setTimer(timerId);
    }
  };

  return <button onClick={toggleTimer}>{timer ? "Stop" : "Start"}</button>;
};

export default App;

More on that in a second!

Click the button, and we call the function toggleTimer. Because timer starts as undefined, our if statement is falsy. So, let’s read the else.

import React, { useState } from "react";

const App = () => {
  const [timer, setTimer] = useState(undefined);

  const toggleTimer = () => {
    if (timer) {
      clearInterval(timer);
      setTimer(undefined);
    } else {      const timerId = setInterval(() => console.log("tick"), 1000);      setTimer(timerId);    }  };

  return <button onClick={toggleTimer}>{timer ? "Stop" : "Start"}</button>;
};

export default App;

Here we call setInterval, passing our ticking function and a delay of 1,000 milliseconds (that’s one second for the history majors). Every second, our tick fires.

setInterval has a useful return— an identifier for itself. This will be a numeric non-zero value like 2202. We’ll need that later to stop the tick, so we save it with setTimer.

Having timer in state can be really handy; anytime we want to know if the world is advancing, or the stopwatch is running, or whatever, the existence of timer can answer that question for us. That’s how we’re changing the call to action from “Start” to “Stop.”

What happens the next time we call the function? timer is truthy, so we execute the if:

import React, { useState } from "react";

const App = () => {
  const [timer, setTimer] = useState(undefined);

  const toggleTimer = () => {
    if (timer) {      clearInterval(timer);      setTimer(undefined);    } else {
      const timerId = setInterval(() => console.log("tick"), 1000);
      setTimer(timerId);
    }
  };

  return <button onClick={toggleTimer}>{timer ? "Stop" : "Start"}</button>;
};

export default App;

Here, we use that same identifier, 2022, to clear the timer. And, we set its value in state to undefined.

What if I want a timer with no user interaction?

What if we want this timer to be running without user interaction? useEffect accomplishes this nicely.

import React, { useEffect } from "react";

const App = () => {
  useEffect(() => {
    const timerId = setInterval(() => console.log("tick"), 1000);
    return () => {
      clearInterval(timerId);
    };
  }, []);

  return <div />;
};

export default App;

We no longer need a state variable to store our timerId because useEffect has that in scope. Note the function we return, which makes sure the interval is cleared when the component unmounts.

import React, { useEffect } from "react";

const App = () => {
  useEffect(() => {
    const timerId = setInterval(() => console.log("tick"), 1000);
    return () => {      clearInterval(timerId);    };  }, []);

  return <div>Timer</div>;
};

export default App;

Wrapping Up

That’s all there is to it! I hope you can build upon this simple example. To see this in action, check out the source code to my Conway implementation.

What are your thoughts on this? Let me know!


Get better at programming by learning with me! Join my 100+ subscribers receiving weekly ideas, creations, and curated resources from across the world of programming.