Jake Worth

Jake Worth

How to Create a Timer or Polling in React with setInterval

Published: December 29, 2022 • Updated: February 05, 2024 4 min read

  • react
  • javascript

Have you ever wanted to create a timer in a React app? This could be in support of a UI timer or polling. In this post, I’ll explain how to create a timer effect in a React application using hooks and setInterval.

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

import * as React from "react";
import { useState } from "react";

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

  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’s the summary: this code creates a button that reads “Start”. On click, that changes to “Stop”, creating a timer with setInterval ticking once per 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 * as React from "react";
import { useState } from "react";

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

  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, shown here as an empty invocation of useState:

import * as React from "react";
import { useState } from "react";

const App = () => {
  const [timer, setTimer] = useState();
  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 conditional is falsy. So, let’s read the else.

import * as React from "react";
import { 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. 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, etc., 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 * as React from "react";
import { 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.

Polling Example

What if we want this timer to be running without user interaction? Polling a server is a classic use case for this. useEffect accomplishes this nicely.

import * as React from "react";
import { 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 * as React from "react";
import { useEffect } from "react";

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

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

export default App;

This can be extracted as a custom hook!

// src/hooks/usePoll.ts
import { useEffect } from "react";

export const usePoll = (callback) => {
  useEffect(() => {
    const id = setInterval(callback, 3000);
    return () => clearInterval(id);
  }, [callback]);
};

And its use:

import * as React from "react";
import { useState } from "react";

import { fetchData } from "api/data";
import { usePoll } from "hooks/usePoll";

const App = () => {
  const [data, setData] = useState();

  usePoll(fetchData(setData));

  return <div>{data}</div>;
};

export default App;

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!


Join 100+ engineers who subscribe for advice, commentary, and technical deep-dives into the world of software.