Fatal Countdowns

This post was originally published on this site

Adrien Montfort

Nov 24 · 5 min read

Image for post

Over the past weeks, Sorare has been rewriting its React components to improve the performance of countdowns on its English auctions with Context API. Read about some of the performance challenges we encountered and overcame along the way.

If you’re interested in technical challenges like this, please check out our open engineering roles and apply for a position.

At Sorare, the global fantasy football game, we sell our digital trading Cards through English auctions. English auctions mean the highest bidder wins. It also means there’s an end date to the auction process. And since we sell hundreds of Card a day this way, we need to display a lot of countdown timers on our website.

How to display a countdown in HTML?

By definition of a countdown, it updates a lot. Our granularity is the second so we update our timers every second.

The first way that comes to mind is by using setInterval:

This is a perfectly valid solution but when you’ll start having multiple countdown on your page it might slow down a bit. And since JavaScript is mono-threaded your clocks might look like they are not synchronized.

Image for post
wait for it

You could make your gif and then use a JS library like libgif to jump to the right frame.

I think it might be a valid solution if we only showed the auctions when there’s little time left. But we show the auctions days in advance and this means the gif would need to have a huge amount a frames and hence would be super big.

Image for post

Well, you can… And it’s pretty well detailed here.

But to quote the article:

just because you can do something with pure CSS doesn’t mean you should.

What about React?

sorare.com is a React TS application so things are a bit different. Even if you could in theory use plain JS with refs but this is not super idiomatic.


Even though you may not need Moment.js, it is the go-to library for date handling in JS. It also has a React version, react-moment that provides a component dedicated to displaying dates.

For our countdown timer, it’s as simple as writing:

In case you’re interested, you can find the full source code of the test project on our public repository.

The way it works under the hood is with this timer called from componentDidUpdate:

As we’ve seen, it’s not so great to have a lot of timers like that on your page so react-moment, adds this pooledTimer feature.

Instead of having one timer per Component, it is mutualized and updates all registered components every time it runs:

It’s better but it still has drawbacks. Calling update directly on a Component is not super idiomatic either because it will render the component by itself and commit the changes to the browser. One reason why React is fast is because it uses a virtual DOM in which updates are quicker and it commits all the changes of a rendering at once so the browser has only to repaint once.

In the Profiler of the React browser extension it will look like this:

Image for post
Commits… commits everywhere

400 commits in a few seconds interval with 100 timers.

And in Chrome task manager I can see my tab sitting at a steady 15%+ CPU.

Context API

Moment.js is not optimized for tree shaking so in an effort to reduce the size of our initial bundle, the time came to try and move away from the Moment.js dependency.

Moving away from Moment.js meant also removing react-moment and it’s a useful component. It also meant it was also the time to try and reduce the number of paintings in the DOM and thus the CPU level.

To keep a single timer, react-moment uses a static property on the Moment class but there’s another way of providing data to your whole application: the Context API.

The idea here is to have a single timer, running in the Context provider, that provides a tick to every countdown component in the app and prompts them to update.

The ticker Context is as simple as:

(once again the whole test application is available on our public repository)

Writing the Timer Component is as simple as:

intervalToDuration is provided here by date-fns, the library we chose for date manipulation.

But in order to actually compare the performance of the Context vs the Pooled timer, we’ll use Moment.js to display the date by simply changing:

This time the performance extension output looks a bit more like what we can expect with only one commit per second.

Image for post

The CPU consumption of the app has also been divided by 2.

With 1000 timers, the react-moment version cannot keep up and timers are updated once every 3 seconds.

Image for post
feel the lag

With our Ticker Context and 1000 timers, the app behaves just fine!

Image for post

This Ticker Context is now in production on sorare.com! Our initial bundle is smaller, our User’s battery life is longer and developers are happier when they look at the React Performance extension.

In the blockchain world, “Don’t trust, verify” is the mantra. Remember that in software engineering, “Don’t trust, benchmark” is a must when optimizing.

Our team is currently exploring more engineering challenges such as building an infrastructure for a game to be played by millions of users, scaling and optimizing our transactions on Ethereum, and helping third-party developers to retrieve the information they need from our API to build side-games with Sorare Cards. Visit our open engineering roles here if you’re interested in the challenge!

Play Sorare today and join the community on:

Twitter: https://twitter.com/SorareHQ
🔥 Discord: https://discord.com/invite/TSjtHaM
📭 Telegram: https://t.me/SorareFC
🖨️ Reddit: https://www.reddit.com/r/Sorare/
🎁 Invite friends: https://sorare.com/invite

Leave a Comment

%d bloggers like this: