Good performance in React application is easy to achieve and also easy to miss, but missed performance loopholes can be more expensive than we can imagine. React provides some exceptional tools to measure performance, and anything measurable can be better monitored and controlled.
The React profiler is available as part of React devtools in Google chrome extensions, which provides an excellent way to measure and record the performance of the application. At every component level, it ensures the awareness of the performance aspect in the ongoing application development. The new profiler provides a consolidated view of the application components rendered with relative ranking details, grouped by commits in the form of different charts – Flame chart, Ranked chart and specific component charts.
The latest React 16.9 is out, in which the new features and notable bug fixes make developers job easy.
With this new version of React, comes the React Profiler API which is now available to measure component performance directly. It is the API which is also internally used by the devtools profiler. It provides a new way to measure the performance at each of the individual component levels by tracking it programmatically.
With the new release, it is easier to measure and control performance at each component level:
- The frequency of render
- The time for each render
- The base time for the initial mount or worst-case render time
The profiler API is particularly helpful in larger applications to get the performance view and impact of regression on a specific component.
How to Use the React Profiler API?
The new Profiler API can be included as an additional tag around each of the components to measure its performance, by giving it a unique id and including a common handler for render.
1 2 3 4 5 6 7 |
const MyGrid = (props) => { return ( <React.Profiler id="MyGrid" onRender={onRenderCallback}> <IGrid {...props} /> </React.Profiler> ); }; |
Use Profiler tag around any number of components to measure their performance, assign a unique id value to facilitate the identification and thus help to track each component uniquely in the Profiler tree.
Define a generic handler, which is called each time the component is rendered, for the first mount and each subsequent update. This can be defined as a common utility function with the profiling data handling logic and can be used across the application whenever profiling any component.
The handler receives the profiling details which can be used appropriately to measure and track the performance parameters of the component.
1 2 3 4 5 6 7 8 9 10 11 12 |
function onRenderCallback( id, phase, actualDuration, baseDuration, startTime, commitTime, interactions ) { //Logic to handle the profiling details console.log( 'The component', id, ', The phase', phase,', Time taken for the update', actualDuration, baseDuration, startTime, commitTime, interactions); } |
This will appear on the log as below for the first mount and each time the component gets re-rendered:
Let’s look at each of the profiling parameter values available on track.
Parameter |
Purpose |
---|---|
id | The “id” prop of the Profiler tree that identifies the component uniquely in the tree, especially while using multiple profilers. |
phase | This indicates the current phase of render , i.e. whether it is initial mount or update. |
actualDuration | It indicates the actual time spent for rendering the committed update for the component. This value will be indicative of how well the component is making use of memoization and avoiding unnecessary re-renders etc. as it should reduce after initial mount if most of the child components are not changing and using memoization effectively. |
baseDuration | It provides the base estimated and required time to render the entire subtree of the component without memoization and gives the worst-case value for reference. |
startTime | It indicates the timestamp when React started the current rendering of the component. The actual and base duration does not include any asynchronous yield time when React was not working, whereas the start time will be inclusive of any other interactions and asynchronous rendering. |
commitTime | It indicates the timestamp when React performed the commit for the current render phase. The components impacted by the same update will have the same commit timestamp and can be used to group together. |
interactions | This is to provide the interactions which resulted in the update. It provides the interactions which resulted in the update. It will be of the form of a descriptive string (“toggle button clicked”) and an associated timestamp. It is still an experimental API but can be included by explicitly importing the unstable version as shown here to explicitly track events. |
Profilers are light-weight components, it should be carefully used only when required because each profiler adds some CPU and memory overhead to an application. Profiling during development will help to expose evident blunders in the component design like unnecessary re-renders & obvious need for memoization. Profiling is disabled by default in production builds but can be enabled explicitly to measure performance. To get accurate results, it is advisable to run the profiling in production mode, enabling it explicitly by modifying the Webpack configuration file as explained here. Do not forget to undo these changes before the final production deployment to avoid any extra bundle size and CPU/memory overhead.
Conclusion
With tools like React Profiler in DevTools & the profiler API, it is easy to track the performance of each React component and take the necessary steps to improve performance by avoiding unnecessary renders and mutating data, memoizing API/function calls, etc. It would be no less of a crime to ignore these tools during development and struggle later with application performance issues.