Using Timers in React Apps

Ben Looper
Oct 29 · 5 min read

Do yourself a favor — let this never be a problem again

Have you ever wanted a message to only show up for a few quick seconds, rather than stick around and ruin the aesthetic of your app with its bright red font? Or only give a user a certain amount of time to complete some action? Luckily for you, vanilla JavaScript has you covered with the built-in setTimeout and setInterval functions. UN-luckily for you, implementing them in React can be a challenge initially. Your first go round, you’re almost certain to either break the timer or break your app via an infinite rendering loop. Before we get into React though, let’s briefly review how these two functions work.

They both take two arguments — a function to do and a length of time (in milliseconds).

The first, setTimeout, will perform the function once after the time you specified has passed.

Image for post
Image for post
Note — use a callback function to prevent changeColor from running right away

If you want to prevent the execution of the function, calling clearTimeout( ) and passing it the variable assigned to the setTimeout function will cut it off if the function hasn’t already been run.

Image for post
Image for post

The second function, setInterval, performs the function after the time you specified has passed until you tell it to stop.

Image for post
Image for post

To stop the cycle, you must call clearInterval( ), passing in the variable you assigned to the setInterval function.

Image for post
Image for post
Suggestion — run clearInterval once some condition is met

In a normal vanilla JS program, you should be able to use these functions without much difficulty once you understand how they work….

But in React, things can get tricky rather quickly. Here’s a simple timer component in React:

Image for post
Image for post

The counter is set to 10 when the component is mounted. Once it’s rendered and after one second, setTimeout runs the callback function that first checks if the counter is greater than zero and, if it is, decreases the counter by one using setState. Then it returns a div with the time remaining in the counter. This works as a timer because setState causes the component to re-render, which causes setTimeout to be run again, until the counter reaches zero.

This approach works, but there’s a glaring problem — if there is any functionality in the timer’s parent component that causes the timer to re-render…well let’s just say that problems quickly arise.

Image for post
Image for post

In this snippet, if a user clicks the button on line 28, it will run the function on line 12. That function uses setState, which would cause Game’s render block to re-run, which in turn would cause the Timer’s render block to re-run. The result will be a timer that slows down when you click the inflate button, as the timer is reset every time you do so.

But that’s just the beginning of your timer woes. Let’s say you want something to actually happen when your counter hits zero — we’d need to add some action in the else block in our Timer component. We’ll pass it a simple function from the Game component called finishGame, which will setState to adjust how the child components are rendered.

Image for post
Image for post
finishGame sets the state to adjust how it’s children are rendered
Image for post
Image for post
x.props.finishGame(timer) will run when the counter is 0

Makes sense, right? What could possibly go wrong?

If you guessed “an infinite loop that makes your computer’s fan go nuts”, you’d be correct. The counter is stored in state, and it was never updated to not equal zero. Timer gets rendered, which runs finishGame because the counter is at zero, which re-renders it, which runs finishGame, which re-renders it…..until you call uncle and ctrl-C.

The major complication that React brings to running timers is that if they’re re-rendered improperly, they just don’t work. I believe that the simple and safest solution is to actually separate them from the other moving parts of your app. This might sound terribly obvious, but for people with less experience using React it’s not. In vanilla Javascript, it was just a function that you ran. In React, it should be treated as its own concern entirely if you want it to work %100 of the time.

Image for post
Image for post

Here you can see that I’ve rendered the Timer and the Game components separately. On the Timer side…

Image for post
Image for post

After finishing the game, I reset the counter. I’ve also added some simple logic to determine whether or not to even start the timer again.

There’s likely a better solution than this, and I’m sure I’ll learn about it as soon as I’ve published this. Nonetheless, the core takeaways remain:

  • Simplify your timer — have it run a counter, then send a message back when it hits zero
  • Separate your timer as it’s own concern, inside it’s own component
  • Render your timer as little as possible
  • Have tight control over the situations where it is rendered

Thanks so much for reading, I hope I was able to save you a bit of time and stress.

If you have any questions or would just like to chat, visit my LinkedIn.

Check out my Github if you’d like to see my work.

The Startup

Medium's largest active publication, followed by +733K people. Follow to join our community.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store