useTime() React Hook
By: James Fulford
Tags: Programming, Web Development, React Native, React, JavaScript

useTime() React Hook

Countdowns in React are complicated/hard. Let’s use hooks to create a useTime hook.

Time keeps on ticking… Photo by Djim Loic on Unsplash

The Hook (Code First!)

Copy-paste this gist (https://gist.github.com/jamesfulford/7f3311bd918982e68d911a9c70b27415) and give it a try in your codebase. Don’t worry, this article will still be here when you get back.

useTime, getTime, and sample usage in a Countdown component.

This gist currently uses Luxon for handling dates, but the comments show how to convert it to use Moment, or native JS Dates. An epoch integer is also a wise move if timezones are complicated.

PSA: If you’re working with timezones, use a library. Else, this will happen to you and all your friends: (I warned you)

The Problem

Using the current time when rendering a React component can be tricky. The present literature (Medium articles, assorted blogs, and StackOverflow answers) often has 3 problems:

Implementation is Wrong

Working with the current time in React is hard. The common solution of setInterval callbacks incrementing/decrementing state will fall out of sync quickly because of a misunderstanding of setInterval in Javascript, and because of a misunderstanding of state transitions in React.

Directly accessing the current time in the render method will not trigger re-renders when needed, and passing through a prop/context only defers the problem to a different component. Therefore, a solution using state is the right solution.

Some implementations available on blogs, Medium articles, and StackOverFlow answers update state incorrectly. Some countdown implementations will store seconds remaining, then will decrement the seconds remaining every 1000ms using setInterval . This will fall out of sync with actual-time very quickly, depending on:

how long the tab is visible (browsers deprioritize setInterval calls for non-focused pages for performance reasons)

calls for non-focused pages for performance reasons) how complicated the rest of your app is (the thread may not get to the setInterval callback quickly enough if there is a lot of work to do)

callback quickly enough if there is a lot of work to do) the performance specs of the viewing device (mobile devices will execute JS slower, for example)

If you want to learn more, research the “Javascript Event Loop” — several good interview questions revolve around this concept. This is not specific to React.

Getting React-specific, some implementations do not consider the fact that this.setState will not immediately update state. This means the first state update may not have been executed before the second state update was queued. This is a problem if the implementation relies on the previous state to decide the next state, (i.e. time is incremented or decremented), because time will go out of sync (because two duplicate updates were queued).

The solution is either to pass a function to this.setState as shown in the React docs, or (better solution) don’t rely on the previous state to get the current state. Inside the setInterval callback, update state with the most recent time.

No Clean-Up is Done

In a class component, a componentWillUnmount method can clean up the interval id using something like clearInterval(this.state.intervalId) . However, some implementations neglect this important detail, potentially causing memory leaks in the code of those who copy the implementation.

My Lifecycle Methods Are Huge (“It’s not a hook”)!

If a React component gets large (maybe you inherited bad code), it can take quite a lot of scrolling to go from the setInterval call to the clearInterval call and the accessing of the current time from this.state . This violates one of my personal rules of thumb:

Things that are related should be close to each other.

Incidentally, this is the motivation for switching to hooks in React.

I have yet to find a hook that does this for consuming and updating the current time. So please, enjoy my implementation.

By the way, this “Things that are related should be close to each other” rule-of-thumb a special case of one of my personal favorite rules (which has life/moral implications if fully adopted):

Make it easy to do the right thing; make it hard to do the wrong thing.

To learn more about this philosophy, check out one of my earlier articles (hey, you read this far!):