A Simple Way to Implement Timers and Timeouts in Redux

Managing timers in React Redux is often tricky. While trying to implement some features that required a lot of timeouts and time measurement of user interactions, we ended up using a simple approach for timers that I hadn’t seen before, so I thought I’d document it here.

Time in the Redux Store

We originally got the idea by thinking, “Wouldn’t it be nice if the time was just a value in the Redux store?” This would make it easy to do something like display a countdown timer:

const countdownTimer: ({expireTime, currentTime}) =>
  (Launch at {`T-${expireTime - currentTime}`}!);

A simple, but somewhat CPU-intensive, way to do this would be to dispatch an action to update the currenTime in the Redux store–say, with a requestAnimationFrame. Instead, we found an approach that accomplishes effectively the same thing but with a lot fewer dispatches.

Only Update Time When Needed

Our approach was as follows:

  • Store the currentTime in the Redux store. This is a globally shared value that any component needing the time can use.
  • When we want to “start a timer,” we record the time we want it to expire somewhere in the store (so expireTime = currentTime + timeout).
  • Then we do a setTimeout() that will dispatch an action to update the global currentTime with the Date.now() time at the expireTime.
  • To know when a timer has expired, we just have to look at its expireTime and the global Redux store time.

The main advantage of tracking timeouts this way is that we don’t have to be extra-careful to cancel our setTimeout()s if a the user moves to a different part of the app and our component gets unmounted. (This is a major cause of exceptions in React apps.)

It also prevents a lot of race conditions. As it doesn’t matter how many components are dispatching time update actions simultaneously, it’s easy to ensure currentTime only increases monotonically. Plus, it works great for countdown timers and plays nicely with time-travel debugging.

Hope this is helpful to you!