How to use React’s experimental new Profiler feature

React 16.4.0 was recently released! 🎉 🎉 🎉 And it’s times like this that you realize how much of a JavaScript nerd you are for getting excited about minor version releases of your favourite framework. Welp!

If you were to mull over the release notes posted by the React team, it would seem like a mostly boring update. Pointer Events seem pretty groovy, but honestly I had only vaguely heard about them up until now.

Other than that, there’s a bugfix for the new-ish getDerivedStateFromProps method (it’s gonna get called on every render now). I haven’t largely adopted using this yet, so the impact of this update for me personally was pretty low.

Then, buried under the headlines, I saw the announcement that they‘ve added an experimental new unstable_Profiler component. Seeing as my life is pretty unstable_ right now, I decided I’d read the RFC and try it out. 🙃

TLDR;

The folks over at the React team are trying to make rendering asynchronous. This can make it tough to reliably measure how long components are taking to render during mounting/updating. Hence, they are messing around with this shiny new Profiler component.

So what can you use this thing for today?

Well, if you’re super into tracking the performance of your React apps, this is definitely gonna be another good tool to add to your belt. If this is not your thing, you should stop reading this and get back to building cool apps. 😎

Using <Profiler />

Disclaimer: you probably shouldn’t use this in production code yet (I mean it says unstable_ right on it, come on man!), though it apparently does get stripped from prod builds. Later they are looking into a production + profiling build setting which would allow you to profile production code as well.

Basically, Profiler is a Component that you can extract from the default React package. Since it has that funky lowercase/underscore name, which a lot of linters frown upon, you’ll need to do one of the following:

import React, { unstable_Profiler as Profiler } from 'react';
...
const Profiler = React.unstable_Profiler;

Now that you’ve got your Profiler, let’s profile some components! You can wrap any part of your JSX tree in a Profiler to see what’s going on with that part of the tree. The Profiler accepts an onRender function, which is where the details about render time are captured. Here’s a simple counter example:

import React, { unstable_Profiler as Profiler } from 'react';
class ComponentWithProfiling extends React.Component {
state = {
count: 0
};
    logProfile = (id, phase, actualTime, baseTime, startTime, commitTime) => {
console.log(`${id}'s ${phase} phase:`);
console.log(`Actual time: ${actualTime}`);
console.log(`Base time: ${baseTime}`);
console.log(`Start time: ${startTime}`);
console.log(`Commit time: ${commitTime}`);
};
    go = direction => () => this.setState(({ count }) => ({
count: direction === "up" ? count + 1 : count - 1
}));
render() {
return (
<Profiler id="app" onRender={this.logProfile}>
<button onClick={this.go("up")}>☝️</button>
<div>The count is {this.state.count}</div>
<button onClick={this.go("down")}>👇</button>
</Profiler>
);
}
}

Note that you need to give each section of the component tree that you are profiling an id. As you can see below, the onRender method receives a bunch of interesting metrics:

https://7jroojkv30.codesandbox.io/

First off, you can see what phase of rendering it was in (either mount or update), which could maybe be used to identify parts of your tree that are updating unexpectedly (much like the excellent why-did-you-update package, which I’ve used many times and highly recommend).

Next, we get the actualTime and baseTime. These are related to the actual time React spends making rendering calculations; ie. figuring out what’s changed. Notice that the actual time for the initial mount takes longer than the updates. This is because on the initial mount, technically everything is “new”. Whereas with updates, calculations should be cheaper because hopefully components in the tree are being updated only if they actually need to be (ie. when prop/state values have changed).

In a larger/real-world application, these values could help expose parts of your app where shouldComponentUpdate is being under/incorrectly utilized, places where prop refs change a lot/“new” props are being passed down, or just places where you weren’t expecting updates to take so dang long.

The final values we get in the onRender are startTime and commitTime. These are essentially a “timestamp” since the initial startup. startTime is the time at which the tree started doing it’s render calculations, whereas the commitTime refers to the time at which React actually committed those changes to the renderer.

If you’re tracking other timestamped events (like clicks or keypresses), then these metrics could help expose deltas between when user-events happen and when renders actually occur. If the gap is wide, this lag may be perceptible to users and should be an area for you to investigate improving performance.

Conclusion

I know for me personally, this tool isn’t gonna be super useful just yet. But it’s one of those things that’s good to know about, because if I do ever hit those performance bottlenecks, this will be a good measuring stick.📏

It’s important to measure your performance problems first, that way when you make “improvements”, you’ll be able to tell if it actually improves things, or if you’re only making things worse. I’ve found that optimizing performance is one of those things that can easily become a time-suck, so make sure that you only try to optimize if you have an actual problem.

I look forward to seeing what the React team does with Profiler going forward. Thanks to @bvaughn for adding this nifty feature!