Friday, November 15, 2024

Profiling Performance of React Apps using React Profiler

 

·

In modern days, web applications are expected to perform fast and responsive. At the same time, these applications are complex and will require significant effort to analyze performance if we do it manually.

That’s why we should search for better tools to reduce the effort. When it comes to React, the React profiler is something you should definitely checkout.

Once you learn how to effectively use this Chrome DevTools plugin, it’ll be a piece of cake to identify performance issues in your React application! 😊

Introduction to React Profiler

If your application is using React 16.5 or above, it will support the new DevTools Profiler plugin. You can view the Profiler tab in DevTools. I’ll be using the Chrome browser for referencing in this article.

Pre-requisites:

  • The application should have React version 16.5 or above.
  • The React DevTools Extension needs to be installed in your browser.
Profiler Tab in Chrome DevTools

If your application supports React Profiler, you can see the above initial screen in DevTools under the ⚛ Profiler tab.

This uses React’s experimental Profiler API to collect timing information of each component render. We can identify any performance bottlenecks using that information.

The Profiler tab is very convenient as it provides a visual representation of render times and allows navigation through commits while viewing props and states of components. As a result, it allows for faster analysis and investigation of performance issues.

In this article, we’ll be looking at two main topics.

  1. How do we performance profile a React application?
  2. How do we interpret the performance data obtained?

Performance Profiling a React application

Profiling a React application is very easy. It involves only 3 steps.

  • Click the Record button in the Profiler tab
  • Use your application as you usually would. (The Profiler will gather information about application re-renders at this stage)
  • Click the Record button again to finish recording (You may also have a separate Stop button to finish recording based on the browser you use).

I have included an image of the Record and Stop button for your reference.

Profiler tab view when profiling is in progress

That’s it! This is the output I got for my demo application

The output obtained after performance profiling a React application

Like I said, profiling a React application is very easy. Now let’s see how we can read the performance data we obtained. This is the most critical part, as this would give you a comprehensive performance analysis of your application components.

Tip: Build Great Design Systems and Micro Frontends

Take frontend development to the next level with independent components. Build and collaborate on component-driven apps to easily unlocks Micro Frontends, and to share components.

OSS Tools like Bit offer a great dev experience for building and composing independent components, and build solo or together with your team.

Give it a try →

An independently source-controlled and shared “card” component. On the right => its dependency graph, auto-generated by Bit.

Reading Performance Data

Usually, React works in two phases: the render phase and the commit phase.

While the render phase determines what changes need to be done to the DOM, the commit phase is where the actual difference is applied to the DOM.

The React profiler collects information in the commit phase and groups the performance info by a commit. The commits are displayed in a bar chart as below.

Commit bar: Currently selected commit is colored in black

React Profiler provides 4 types of chart views for performance data.

  1. Flame Chart
  2. Ranked Chart
  3. Interaction Chart
  4. Component Chart

1. Flame Chart

This view represents the state of your application for a single commit. Each bar represents a component, and the bar’s length shows how long it took to render the component. In my example, the Counters component has taken longer to render than the Counter Key component. By clicking on each bar, you can get the specific rendering information for that component for the selected commit on the right detail panel.

Using the bars’ colors, you can get an idea about the time each component took to render. The general colors are as follows.

  • Yellow — The component took a lot of time to render
  • Blue — The component comparatively took less time to render
  • Gray — The component did not render at all during the commit

Using this, you can identify which components are the culprit for the long renders and optimize those components with techniques such as memoization to avoid unnecessary re-renders.

2. Ranked Chart

When you click the “Ranked” button in the Profiler tab, you can get the Ranked chart view. This view shows the order of components based on the time they took to render. The components which took more time will be on the top. At a glance, this view gives an idea about which components are the bottlenecks and affects the most for page reloads.

In my demo application, the Counters component has taken the most time to render.

3. Interaction Chart

This is an experimental API that can trace the cause of an update in your application.

For example, if you scroll to a certain position in your application, this would be traced as an interaction and will be displayed in the Profiler tab. The commits related to this interaction will be displayed for each interaction as well.

This was added to React very recently. You can refer to this API here to find out more about it.

4. Component Chart

Double-clicking on a component will allow you to view the component chart. This view will provide information about the component lifecycle during the profiling time. The information is displayed in the form of a bar chart where each bar shows at what time the particular component was rendered and the length of the bar shows for how long it was rendering.

Now that you’ve learned how to read performance data, let’s see what points you should keep an eye out for when using this tool to analyze your applications.

Common Pitfalls to Avoid when using React Profiler in Practice

Measuring Performance on Development vs Production Build

Measuring the performance of your application in development mode may give you sugar-coated results.

In order to get an accurate measurement, you need to profile your application in production mode, as this is what the users would experience.

However, measuring this would not be easy and straight-forward as React has code in it that’s specific to profiling, and they remove it from the production build. As a result, you may see a message saying, “Profiling support requires either a development or production-profiling build of React v16.5+”.

In order to profile the production build of your application, you will have to update your Webpack configuration to alias imports of specific modules to its profiling version. The two aliases you need to add are for the react-dom and scheduler/tracing modules.

The profiling version of these two would be react-dom/profiling and scheduler/tracing-profiling . Once this is done, you will be able to get performance profiled data for your production build as well.

Another factor to keep in mind is that some users might be using devices with limited resources to view the web app. Therefore profiling in a high-end machine would not represent the common use case.

To overcome this you can use Network and CPU throttling in DevTools to check how well your app performs in low-end or mid-end devices.

Problems with React Profiler

Multiple Roots Issue

If you have multiple roots in your application, it can lead to an error. The Profiler may display an error saying, “No profiling data has been recorded for the selected root.” You need to select a different root to see whether any data has been recorded for this problem.

Source: Reactjs.org

Render Thresholds

When a commit is extremely fast, performance.now() does not give the Profiler any meaningful timing information.

Source: Reactjs.org

Alternatives to React Profiler

The DevTools extension is not the only way you can measure the performance of your application. You can use the <Profiler> React component (React’s Profiler API) in your code to measure performance as well.

Further, you could also use the Chrome DevTools Performance tab or Lighthouse to measure performance in general as well.

The React Profiler’s specialty is that it is customized for React applications and is therefore very convenient over other alternatives.

Summary

The latest addition to React 16.5, the React Profiler, gives developers a convenient way to analyze their React applications’ performance bottlenecks visually. This is one of the most powerful tools to inspect the performance. However, when working with a production bundle, you need to have a few extra configurations to get this to work.

Having access to performance data in your application gives you full control to optimize your application and provide the best user experience.

Thanks for reading!

Monday, November 11, 2024

React Profiler: A Step by step guide to measuring app performance

 

As react applications are becoming more and more complex, measuring application performance becomes a necessary task for developers.

React provides a built in performance monitoring tool called the react profiler

React profiler allows developers to measure individual component performance in their applications

The profiler measures the time it takes to render a component and provides critical insights with regards to the components rendering behaviour including how often the component renders and the cost of rendering the component

Dead Simple Chat allows you to easily add Chat to any React Application using powerful Chat API and SDK.

profiler lets you measure rendering performance of a React Tree programmatically.

<Profiler id="App" onRender={onRender}>
  <App />
</Profiler>

Rendering performance Measurement

To measure rendering performance of a react component just wrap it inside ht component

like so

<App>
  <Profiler id="navbar" onRender={onRender}>
    <Navbar />
  </Profiler>
  <PageContent />
</App>

It takes two props

  1. onRender function: React calls this function anytime the component within the tree commits an update
  2. id: A string id a

Note: Profiler component lets you gather measurements programtically. If you are looking for a UI based profiler the Profiler tab in react development tools will work for you

Dead Simple Chat allows you to easily add Chat to any React Application using powerful Chat API and SDK.

onRender Callback

the onRender callback receives several parameters that provide detailed information about the components rendering behaviour and resource utilization

function onRender(id, phase, actualDuration, baseDuration, startTime, commitTime) {
  // information regarding rendered components...
}
  1. id: It is a unique identifier for the profiler. id is a string prop that lets you identify the profiler tree that has been just committed or which part of the tree was committed if you are using multiple profilers
  2. phase: mount, update or nested update. Lets you know whether the tree has been mounted for the first time or re rendered due to a change in props state or hooks
  3. actualDuration: time spent in milliseconds rendering the and its children for the current update. Ideally this should decrease after the first render and not many components need to re render if specific props change
  4. baseDuration: the amount of time in milliseconds that would be required to re render the entire profiler subtree. It is calculated by adding recent render durations of each component in the tree. It is a worst case cost of rendering
  5. startTime: A timestamp indicating when react started rendering the components current update
  6. endTime: A timestamp indicating when react stopped rendering the components recent update.

Dead Simple Chat allows you to easily add Chat to any React Application using powerful Chat API SDK.

Measuring different parts of an Application

the <Profiler> component can be used in multiple places to measure different parts of an application

<App>
  <Profiler id="navbar" onRender={onRender}>
    <Navbar />
  </Profiler>
  <Profiler id="Content" onRender={onRender}>
    <Content />
  </Profiler>
</App>

You can also nest <Profiler> components

<App>
  <Profiler id="Navbar" onRender={onRender}>
    <Navbar />
  </Profiler>
  <Profiler id="Content" onRender={onRender}>
    <Content>
      <Profiler id="Editor" onRender={onRender}>
        <Editor />
      </Profiler>
      <Preview />
    </Content>
  </Profiler>
</App>

Although profiler is a lightweight component. It does add some CPU overhead and should be used when necessary.

Examples

1. Data table component

Consider a data table component that displays large number of records. Whenever new data is fetched the data table is updated, potentially causing performance issues

let us try to find the bottlenecks using the profiler component and fix the issues

import { Profiler } from 'react';
import TableInfo from './TableInfo';

function App() {
  return (
    <Profiler id="TableInfo" onRender={onRenderCallback}>
      <DataTable />
    </Profiler>
  );
}

function onRenderCallback(
  id,
  phase,
  actualDuration,
  baseDuration,
  startTime,
  endTime,
) {
  console.log(`Profiler [${id}] - ${phase} - ${actualDuration} ms`);
}
  1. Wrap the component in the profiler component and provide and id and an onRender function on callback
  2. The onRenderfunction gives you the following parameters when the table renders on screen
  3. id,
  4. phase,
  5. actualDuration,
  6. baseDuration,
  7. startTime,
  8. endTime,
  9. log the total time and phase to the console and note the readings then

Add the profiler to the child components as well like

import { Profiler } from 'react';
import ChildComponent from './ChildComponent';

function ParentComponent() {
  return (
    <div>
      <Profiler id="ChildComponent" onRender={onRenderCallback}>
        <ChildComponent />
      </Profiler>
    </div>
  );
}

Analyze the performance of each child component using actualDuration and phase and startTime and endTime.

2. Measuring the performance of a Form Component with validation

In this example we have a complex form with multiple input fields and form validation. This form can have performance issue when validation is triggered on each input change

import { Profiler } from 'react';
import ExpensiveForm from './ExpensiveForm';

function App() {
  return (
    <Profiler id="ExpensiveForm" onRender={onRenderCallback}>
      <ExpensiveForm />
    </Profiler>
  );
}

function onRenderCallback(
  id,
  phase,
  actualDuration,
  baseDuration,
  startTime,
  commitTime,
  interactions
) {
  console.log(`Profiler [${id}] - ${phase} - ${actualDuration} ms`);
}

3. Infinite scroll: Identify performance issues using profiler

we have a list component that displays data using infinite scroll functionality

There might be performance issues with excessive re rendering of data. We will be using react profiler to debug these issues

import { Profiler } from 'react';
import InfiniteScrollList from './InfiniteScrollList';

function App() {
  return (
    <Profiler id="InfiniteScrollList" onRender={onRenderCallback}>
      <InfiniteScrollList />
    </Profiler>
  );
}

function onRenderCallback(
  id,
  phase,
  actualDuration,
  baseDuration,
  startTime,
  commitTime,
  interactions
) {
  console.log(`Profiler [${id}] - ${phase} - ${actualDuration} ms`);
}

Let us analyze the performance data using the react profiler. analyze and debug unusually long re renders of the list in the InfiniteScrollList component

**debounce **the scroll event: Delay the rate at which the scroll handler finds new items to show

memorization: React.memo() can be used to prevent un necessary re renders

4. Expensive Computation: Analysing performance

Let us take a component that does expensive computation. Maybe it has a large data set or some other reason for being computationally intensive

We want ot analyse the component and identify performance bottlenecks

  1. We need to wrap the expensive computation component inside a Profiler component with an id and a onRenderCallback function
import { Profiler } from 'react';
import ExpensiveComputation from './ExpensiveComputation';

function App() {
  return (
    <Profiler id="ExpensiveComputation" onRender={onRenderCallback}>
      <ExpensiveComputation />
    </Profiler>
  );
}

implement the onRenderCallback function to retun the performance data collected by the profiler like so

function onRenderCallback(
  id,
  phase,
  actualDuration,
  baseDuration,
  startTime,
  endTime
) {
  console.log(`Profiler [${id}] - ${phase} - ${actualDuration} ms`);
}

Just optimize the application and run the profiler again.

Conditional rendering: Analyzing performance

We have a component with several children and they render conditionally based on the components state.

We need to analyze the rendering performance of these child components to ensure that they render only when necessary

import { Profiler } from 'react';
import ComponentConditionalRendering from './ComponentConditionalRendering';
import ChildRE from './ChildRE';
import ChildCD from './ChildCD';

function App() {
  return (
    <Profiler id="ComponentConditionalRendering" onRender={onRenderCallback}>
      <ComponentConditionalRendering>
        <Profiler id="ChildRE" onRender={onRenderCallback}>
          <ChildRE />
        </Profiler>
        <Profiler id="ChildCD" onRender={onRenderCallback}>
          <ChildCD />
        </ComponentConditionalRendering>
    </Profiler>
  );
}
function onRenderCallback(
  id,
  phase,
  actualDuration,
  baseDuration,
  startTime,
  endTime
) {
  console.log(`Profiler [${id}] - ${phase} - ${actualDuration} ms`);
}

Lets wrap the component in the component and provide the profiler with an id and an onRender function

the onRender function will return the following parameters : id,
phase,
actualDuration,
baseDuration,
startTime,
endTime

analyze the performance and make improvements if needed and then re run the profiler to check if everything is working as it is supposed to

Dead Simple Chat allows you to easily add Chat to any React Application using powerful Chat API SDK.

React Profiler vs React DevTools Profiler

  • React Profiler is a component which can measure specific components of a react application
  • Devtools profiler is a visual profiler and part of the react devtools chrome / browser extention
  • You need to wrap the react Profiler around a component to gether data with regards to the component
  • React Dev tools profiler is a complete visual profiler into the performance of an react application

Benefits of using react profiler

  • Performance Bottlenecks identification: You can measure rendering performance of individual components with the profiler component. This enables you to find out what is creating a performance bottleneck and fix the issue
  • **Monitoring component rendering behaviour: **You can find out what unnecessary renders are happening and optimize components by reducing their rendering costs by monitor how often a component is rendering and what is the cost of each render
  • Performance data visualization when used with devtools react profiler helps visualize the performance of react applications making it easy to understand, identify and resolve performance issues

Disadvantages of react profiler

  • Only works in development mode: The react profiler only works in the development mode and thus you can measure the performance in production. To measure performance in production need to enable a special production build with profiling enabled
  • Performance overhead during profiling: recording performance requires cpu and resources although profiler tries to be easy on the resources but does consume resources during profiling

Disadvantages of react profiler

  1. **Only works in development mode: **The react profiler only works in the development mode and thus you can measure the performance in production. To measure performance in production need to enable a special production build with profiling enabled
  2. **Performance overhead during profiling: **recording performance requires cpu and resources although profiler tries to be easy on the resources but does consume resources during profiling

Conclusion

In this article we explained how the react profiler works. React profiler is an excellent tool to measure performance of a react application

You can optimize component specific performance also react profiler can be nested within other components

  • Purpose: React profiler is a performance measurement tool helping you analyse react component, identify bottlenecks and reduce unnessasory re renders
  • Built in profiler: React dev-tools has a built in profiler which provides a visual interface for analyzing rendering performance of components. You can use react profiler in congulation with the dev tools profiler
  • : The profiler component can be used to measure the performance of a specific component. Profiler components can be nested as well. Profiler takes two props an id and a onRenderCallback function
  • onRenderCallback: onRenderCallback is a function that you pass to the component. It returns various performance metrics that can be used to measure performance

Profiling Performance of React Apps using React Profiler

  · In modern days, web applications are expected to perform fast and responsive. At the same time, these applications are complex and will ...