11/27/2018 · react native

Performance Tracking in React Native

Motivation

Whenever I happen to work on optimizing some component to perform better, I want to see results in metrics, and ideally from users.

Unfortunately in previous versions of React Native, I could only measure performance on my own device or simulator.

In the recent React Native v57 release I discovered a nice hefty component:

<Profiler id={"ProfileComponent"} onRender={this.onRender} />

The Profiler component will provide us metrics for all wrapped components.

<Profiler id={"ProfileComponent"} onRender={this.onRender}>
  <View>
   <Text>Hi</Text>
  </View>
</Profiler>

The best part about this component is not the ease of seeing metrics for our components, but the fact we can use it in production!

via GIPHY

Just be aware that currently, the Profiler component is not yet stable.

Let’s get started with implementing a simple example of Profiler and Firebase Performance Tool.

Requirements 👈

  • React Native ^0.57.3 🔪 (also possible since 0.56, but haven’t tested)
  • Ejected React Native project
  • Tool to record metrics (ie.: Firebase Performance Tool)

Simple implementation 🤓

1. Add alias for react-dom/profiler

To make profiling work in a production environment, we need to add an alias for react-dom/profiler and scheduler/tracing to .babelrc file.

Before we edit the file, we need to install this package babel-plugin-module-resolver , which bring us alias functionality.

yarn add -D babel-plugin-module-resolver

Once we got the babel plugin installed, we modify .babelrc like this:

2. Add Profiler component

We will import unstable_Profiler from React and make an alias to make our code more readable.

Implementing Profiler itself is very straightforward. Just wrap any part of code you want to measure.

Last step is to listen for onRender callback and console.log metrics with logMeasurement function. As you can see, the function is asynchronous–this part is for when we later implement Firebase Performance.

Here is a quick overview of what onRender function will return:

  • id: string - The id value of the Profiler tag that was measured.
  • phase: “mount" | “update"- Identifies whether this component has just been mounted or re-rendered, due to a change in state or props.
  • actualDuration: number - Time spent rendering the Profiler and its descendants for the most recent "mount" or "update" render.
  • baseDuration: number - Duration of the most recent render time for each individual component within the Profiler tree.
📖 For more details, please refer to official RFCS of Profiler

Add Performance Monitoring

To record Profiler metrics, I picked Firebase Performance Tool (implemented in RN via Invertase package)

I’m putting the final code upfront, so I can go through each part separately.

Since I’m initializing trace in componentDidMount I have to cache values from initial render in variables initialMount and initialUpdates, and later record those data (line 21, 22).

I’m also using the traceStarted variable to track if trace has started. The current implementation of Firebase Perf Tool is asynchronous. The good news is that in an upcoming release, the perf tools should become synchronous (I would keep an eye on those release notes).

To keep this example very simple, I’m recording the mount time as initialMount (if components re-mount, it will record last mount) and counter as updates, measuring the number of times component re-renders.

On top of the initialMount and update, we are also able to record how long each re-render takes. It almost seems like the possibilities are almost endless 🤩

With this simple approach, we can add performance tracking to our most important components.

To make code more reusable, we can create our own higher-order component (HOC) for the Profiler component, and include all of our additional logic.

Should I use it in production? 🙇‍

This is a tricky question at the time of writing this article, since the whole feature is still flagged as unstable, and React is not using asynchronous rendering.

We can expect some minor drawbacks when using the profiler in production. Let’s take a closer look at two issues, with which I’m mostly concerned: how much additional commit time will Profiler adds to our components, and how much our bundle size will increase.

Is Profiler suitable for production?

via GIPHY

To answer this question, we can try to measure impact before/after adding <Profiler> component by using React Developer Tools Profiler.

My test case consist of FlatList with native-base <Card /> component rendered 10x.

For test purposes, I created two components with identical code. The only difference was a <Profiler /> component added to <ProfilerComponent />.

Example of real data from one test run

I ran this test 10x for each component, and measured the average commit time for the component.

  • commit time with Profiler:174ms
  • commit time without Profiler: 166ms

As we can see, the Profiler component added roughly about 5% of additional commit time.

This result was above my expectations.

Bundle size

If we take a look at the table below, we can see that using the react-dom.profiling bundle will increase size of our bundle very marginally – less than 1kB.

Size comparison of different react-dom bundles

Conclusion

Based on those two results, I’ll not hesitate to use Profiler in production.

The only thing to keep in mind, that overuse of Profiler will inevitably lead to slower performance.

Happy Profiling times! 🎄

Resources

All resources I used to gather information about Profiler:


What are your thoughts? If you find this article useful don’t forget to share and give some kudos! ❤️️️️️
Lukas Kurucz

Lukas Kurucz

Frontend dev with ❤️#ReacNative #CSS #UI Follow me on Twitter|Medium