Measure Performance with the New React Profiler Component

Learn how to monitor your components’ performance with the new ReactJS Profiler component.

Over the years React has evolved drastically. We have seen the addition of awesome features like memorization ( React.memo ), SSR, React.lazy , suspense, and hooks.

Now, a new toy is available for us to play with, the Profiler component. In this post, we will look at this component, what it does and how to use it.

The Profiler component

The Profiler component is a built-in React component. It is used to profile/measure the time taken to "mount" or "update" a section of a component subtree.

It should be noted that the Profiler component is still in an unstable stage and should be used with care.

As a side note, I should say I’m all for testing, monitoring and optimizing components but for that to make sense —maximize components reuse by building them to be independent and reusable.

You can also use cloud services like Bit.dev to “harvest” components from your different projects and publish them to Bit’s component hub. This way you and your team, maximize code reuse, speed up development and always use performance-optimized (and stable) components.

Example: browsing through shared components @ bit.dev

Before we start — Try it out, hands-on

I’ve made a small demo in Codesandbox.io. It consists of two components Counter1 and Counter2 each wrapped in a separate Profiler component with ids “Counter1” and “Counter2” respectively. I made their time metrics to visually displayed whenever they mount or update. Try it out below:

To use the Profiler component:

import React from "react";

const Profiler = React.unstable_Profiler

The React object has the unstable_Profiler component as property in it. So we just refer to it as Profiler. The unstable_ prefix denotes that the component is still unstable and prone to changes.

Now, with this Profiler, we can wrap our component(s) in it.

<Profiler>

<Counter />

</Profiler>

The Profiler will measure the time taken to render the Counter component. Also, when the Counter is re-rendered the Profiler will measure the time taken to do so.

<Profiler>

<Counter>

<Count />

</Counter>

</Profiler>

<Footer />

Here, the Profiler will measure the time taken to render the Counter component and its descendants, which is the Count component. The Footer component rendering time is not calculated here, because it is not within/enclosed in the Profiler tags.

Only the children components of a Profiler are inclusive in the time metrics calculation.

Multiple Profiler component can be used at once.

<Profiler>

<Counter>

<Count />

</Counter>

</Profiler>

<Profiler>

<Footer />

</Profiler>

The first Profiler will calculate time metrics for Counter and its children, while the second Profiler will calculate for the Footer component. Each reporting separate results for each.

Profiler can be used anywhere in the component tree and can even be nested.

<Profiler>

<Counter>

<Profiler>

<Count />

</Profiler>

</Counter>

</Profiler>

<Profiler>

<Footer />

</Profiler>

This Profiler component takes some props:

id : This is to identify the Profilers, so as to know which Profiler is reporting.

<Profiler id="Counter">

<Counter>

<Count />

</Counter>

</Profiler>

<Profiler id="Footer">

<Footer />

</Profiler>

onRender : This is a function prop, it takes different parameters.

id : The id prop set on the Profiler component.

: The prop set on the component. phase : This will report whether the component is the "mount" phase or "update/re-render" phase.

: This will report whether the component is the "mount" phase or "update/re-render" phase. actualTime : The time it took for the Profiler to mount or update its descendants.

: The time it took for the Profiler to mount or update its descendants. baseTime : The time it took each individual component in the Profiler tree to mount or update.

: The time it took each individual component in the Profiler tree to mount or update. startTime : The time the Profiler started measuring the mount/render time for its descendants

: The time the Profiler started measuring the mount/render time for its descendants commitTime : The time it took to commit an update.

Example

Let’s look at a simple example:

class Counter extends React.Component {

constructor() {

this.state = {

count: 0

}

} render() {

return (

<div>

Count: {this.state.count}

<button onClick={() => {this.setState({count: this.state.count + 1})}}>Incr</button>

</div>

)

}

} class App extends React.Component {

constructor(props) {

super(props)

} callback(id, phase, actualTime, baseTime, startTime, commitTime) {

l(id, phase, actualTime, baseTime, startTime, commitTime)

} render() {

return (

<Profiler id="Counter" onRender={this.callback}>

<Counter />

</Profiler>

)

}

}

We have a component Counter enclosed within the Profiler component. The Profiler is assigned an id "Counter" and an onRender prop with a callback method assigned to it. In the callback method we are logging the parameters.

Serve the app npm run start and load it in your browser localhost:3000 . Open your Console DevTool, you will see this logged:

Counter mount 10 2 1898 1911

“Counter” is the id assigned to the Profiler this shows it is the Profiler wrapped in the Counter component is logging. “mount” denotes the phase, here it is in the “mount” phase.

“10” is the time spent mounting the Counter component because it is in the "mount" phase.

“2” is the time spent rendering the Counter component.

“1898” is the start time at which the Counter component started doing its render calculations.

“1911” is the time where React committed the Counter changes to the renderer.

Now, click on the Incr button, the state count of the Counter component will be incremented to 1. The Counter will re-render. In the Console, we will see another metrics log:

Counter update 3 2 40872 40877

Now, the phase is an “update” because the Counter was re-rendered when we clicked the Incr button.

compare it to the mount metrics: Counter mount 10 2 1898 1911 . We will see that it took a lesser time in the actualTime during the update this is because on initial mount everything is new, whereas, in an update, calculations are cheaper because it updates the pieces of DOM nodes that changed.

Live project

Usefulness

The Profiler will be of immense in real-world apps. It will help you detect parts of your app where optimization should be applied or where it is wrongly applied. It will detect places that take very long to update.

This would help you to apply any optimization tricks in the area to boost your app and your user’s experience.

Conclusion

This is another tool for your React toolbelt.

If you have any questions regarding this or anything I should add, correct or remove, feel free to comment, email or DM me.

Thanks !!!

Credits

Learn More