Timers in React Apps with Redux

Gustavo Machado
3 min readDec 20, 2016

--

tl;dr; there are several different ways to handle the use of timers in React applications with redux. In this post I go through three different strategies and when to use which.

The other day in the meetupjs slack community we had a long and very interesting conversation about how and when to use timers in React applications, and more specifically when using redux. Here is a summary of some of the comments and conclusions.

Component’s internal timer

There’s nothing preventing you from using a timer inside of a component. You can either start the timer on componentDidMount or as a result of some event on the controls of the component, and then stopping the timer in the componentWillUnmount and/or as a result of some other event. For example:

export default class Loading extends Component {state = {
timer: null,
counter: 0
};
componentDidMount() {
let timer = setInterval(this.tick, 1000);
this.setState({timer});
}
componentWillUnmount() {
this.clearInterval(this.state.timer);
}
tick() {
this.setState({
counter: this.state.counter + 1
});
}
render() {
<div>Loading{"...".substr(0, this.state.counter % 3 + 1)}</div>
}

Some pros:

  • Simple and straight forward.
  • Component can be self-contained.

Some cons:

  • State is internal which makes it harder to share with other components.
  • State is internal which makes it harder to store and hydrate.
  • Specific to redux, but the this would not generate actions nor be stored in a central store.

Timers in Actions

Another alternative is to trigger the timer as part of dispatching an action. An example of this using redux-thunk could look like this.

let timer = null;
const start = () => (dispatch) => {
clearInterval(timer);
timer = setInterval(() => dispatch(tick()), 1000);
dispatch({ type: TIMER_START });
dispatch(tick())
}
const tick = () => { type: TIMER_TICK };const stop = () => {
clearInterval(timer);
return { type: TIMER_STOP };
}

Some pros:

  • You can track actions and state mutations related to the timer.
  • You can share the timer state all throughout your application.

Some cons:

  • You still would have to handle “re-starting” the timer when your apps is closed/opened.
  • It adds a bit of complexity to the code compared to the previous sample. If your app is already using redux, then this may not be as bad.

Global timer

Somewhere in your code, you could have a global timer started as soon as your application loads “watching” for states of your app that requires to trigger further actions. For example:

//somewhere when you app starts
setInterval(()=> {
let actions = calculate_pending_actions(store.getState());
actions.forEach(dispatch);
}, 50); //something short
function calculate_pending_actions (state) {
let { timer } = state;
let actions = [];
// put all your conditions here...
if (timer.started && (new Date()).getTime() - timer.startedOn > 60*1000) {
actions.push(stop());
}
// etc...
return actions;
}

This last option is by far the most complex, and is somewhat similar to how games are architected (although I would not necessarily recommend redux for games!).

Some pros:

  • No need to handle start/clear timers, there’s only one timer running all the time.
  • All the timers logic of the entire app is in one place.
  • This plays well with “reactive” libraries like mobX or rxjs.
  • Easy to test “calculate_pending_actions” (it depends on a simple object, and returns an array of actions).
  • “re-hydration” becomes trivial, load the state on the store before starting the timer, and that might be all!

Some cons:

  • It’s the most complex approach.
  • It might get tricky to dispatch actions at specific intervals.

Conclusion

As a rule of thumb, I always try to go with the “simplest” approach, which depending on your specific application it might be any of the three approaches outlined above.

Do you have any other techniques or pros/cons for the ones I showed???

--

--