Say hello to the new perf hotness - a profiler built into React. This announcement blog post shows how to use it as a Chrome Extension, but you can also use it in your code, to get insight into real world interactions, even in production. Let's see how.
Create a new react app
If you're not familiar, there's a create-react-app
tool that gets you started quickly with a new app.
npm i -g create-react-app npx create-react-app my-app cd my-app npm start
Boom! A new app is born, showing a rotating logo.
Stop spinning
Now let's make that logo start/stop spinning on click. Tweak the CSS first.
Before:
.App-logo { animation: App-logo-spin infinite 20s linear; height: 40vmin; pointer-events: none; }
After:
.App-logo-spin { animation: App-logo-spin infinite 20s linear; }
Then handle the click:
Before:
<img src={logo} className="App-logo" alt="logo" />
After:
<img src={logo} className={this.state.spin ? 'App-logo App-logo-spin' : 'App-logo'} alt="logo" onClick={() => this.setState({spin: !this.state.spin})} />
... which also requires a spin
state:
constructor(props) { super(props); this.state = { spin: true, }; }
Now clicking on the logo toggles the spinning.
Profile
Profiling this interaction (and any other in your app) is simply a question of wrapping the interesting part (even your whole app) in a Profiler
component. Let's wrap everything, which means something like:
// require import React, {Component, unstable_Profiler as Profiler} from 'react';
And then wrap:
<Profiler id="profi" onRender={onRender}> <div className="App">etc.</div> </Profiler>
So you give the profiler an ID since you can have multiple interesting parts of the app profiled, and an onRender
callback.
Now this callback is invoked every time ReactDOM does a "commit" to the actual DOM. Which is usually the expensive part, and something you want to do as fewer times as practical. And what goes into the callback? Here's an example:
function onRender(id, phase, actualDuration, baseDuration, startTime, commitTime) { if (id !== 'profi') { // optional return; } console.log(arguments); }
So now what happens when you load the app and click the logo twice? You see the initial rendering (phase: mount) and the two updates (phase: update)
["profi", "mount", 5.500000013853423, 1.0450000263517722, 10696.320000002743, 10703.885000009905] ["profi", "update", 0.9149999968940392, 0.37500001781154424, 21110.35499999707, 21111.57500000263] ["profi", "update", 0.37000000884290785, 0.14500002725981176, 24351.725000000442, 24352.49499999918]
What do these numbers mean? The first two are durations (actual and base) explained here. The other two are when the commit is starting when it's done, explained here.
And finally: the profiling React build, see here. Now in addition to "dev" and "prod" you have "profiling" which is prod+profiling, in other words a fast prod version without all the dev overhead.
And that's about it - time to get profiling!
Comments? Feedback? Find me on Twitter, Mastodon, Bluesky, LinkedIn, Threads