Upgrading DriveTribe to React 16

A 33% speed increase for doing as close to nothing as this profession allows

While writing the upcoming part 4 of my performance deep-dive into the DriveTribe website, I published our first open source project: React Intersection.

I’m using the new module to reinstate lazy-loaded images on the website and measuring the effect that has on load times.

Through a freak misconfiguration (or moment of raw negligence, you decide), I initially set React 16 as a peer dependency. DriveTribe is still built on 15.5, and as 16 is said to bring both filesize and performance benefits (with more on the roadmap), I thought this was a perfect time to ignore my mistake and upgrade instead.

The upgrade process

The DriveTribe website codebase is a big place and I presumed (spoiler: wrongly) that the upgrade process might take as long as a couple of weeks.

This was based on the misconception that React Router would need upgrading from version 2 to 4. Its wildly different API would entail a radical refactor.

Luckily, React Router 3 is compatible with React 16 and only threw a minor roadblock in the need to upgrade React Router Scroll to version 0.4.4.

To give you a flavour of just how minor, this code:

import useScroll from 'react-router-scroll';

Became this:

import { useScroll } from 'react-router-scroll';

What I didn’t do

While writing this blog post I received the following tweet from Damon Bauer:

It’s a great question. React 16 introduces a bunch of new toys to play with, like the aforementioned error boundaries, and fragments.

I’m really excited about the new features, but just as the React team focused on backwards compatibility first, I thought it’d be safer to simply install React 16 while keeping everything else equal.

Any potential bugs arising from the adoption of new features would muddy the source of any bugs created from the upgrade.

However, as a courtesy, I did swap ReactDOM.render for the new ReactDOM.hydrate method, but in a sensible mood I would have left that for a future PR.

This entire process, from branch to merge, took less than an hour. On January 2nd no less! A day when the average person is still experiencing Christmas meat sweats and maybe some New Year Eve heart stuff.

That’s pretty incredible when you consider that 16 is a ground-up rewrite.

Impact

As mentioned, the purported benefits of React 16 are a smaller bundle size (a third smaller!) and faster server-side rendering performance.

Bundle size

As identified in the initial part of this blog series, reducing our bundle size is a priority.

Our main.js did decrease by the advertised amount (15kb), but it’s still a whopping 218kb.

When we created the site, main.js was 100kb and React was about half of that. The flipside of our current situation is that, at the very least, we’re now in a position where the bloat is under our control.

We’ll be exploring ways to reduce and maintain small bundle sizes in an upcoming post. For now, I’m happy to save an amount that could itself fit the entirety of Popmotion, with cola and fries. Large.

Rendering performance

Much has been made about the performance improvements React 16 brings to server-side rendering.

I didn’t measure any notable speed increase in client-side rendering. Server-side, things are indeed a different story.

The render time, which we measure as the duration of the blocking renderToString call was previously 90ms.

Now, that’s down to 62ms. A 33% speed increase. For doing as close to nothing as this profession allows. Doffed caps all round.

This duration is promised to decrease further when we upgrade Node.

The future

React 16 is yet to bear all its fruit, which is promised to be bountiful.

Already, fragments offer the ability to reduce the depth of the DOM, which will reduce memory footprint and improve rendering speeds.

A new renderToNodeStream method will potentially allow us to stream our response rather than wait for renderToString to complete. However, this is contingent on it playing nicely with the rewind functions in React Helmet and Styled Components, which I’ve heard it might not.

In the near future, client-side rendering will benefit from 16’s new asynchronous Fiber architecture, which you can learn more about in cartoon form.

Reductively, it means fewer blocking renders, which will help keep animations and other interactions smooth. Once you’ve made it over the major bump, this will come for free in a future release.

Conclusion

Upgrading React took under and hour, yet shaved off 15kb and reduced server-render duration by 33%.

More importantly, we can now leverage its new features in an incremental fashion, as well as incorporate upcoming Fiber improvements for free.

If you use the now-defunctcreateClass, you’ll need to install this as a separate library. In my personal opinion it’s worth migrating (as a second pass!) as you’ll thank yourself for weening your team off a framework-specific API and onto a native one.

I can’t overstate how effortless the upgrade was. If you’re still a hold-out, find yourself a free morning and cross an easy item off your list to start your year.

Update 5/1/2018: The original version of this article incorrectly stated that users of createClass would face a steeper upgrade path. Dan Abramov got in touch to let us know that you can include support for this as a separate library. The article has been updated.