Optimizing react component

Optimizing react component blog banner image

I am working on date picker component from baseweb in past few days. I found out some serious performance issues. In this blog post I am going to write about the process of finding performance problems in app. This is not specific to react as it only uses the devtools that browser offers. If you are familiar with baseweb and directly want to look at the PR then visit https://github.com/uber/baseweb/pull/4016. These changes are published in v9.107.0, so if you want to see how it was earlier then select the version < = 9.106.0 or click https://v9-106-0.baseweb.design/

Before starting you can play with the datepicker component in baseweb documentation. Here is the image of time it took to re-render when we hover over one date and then other. This is the initial behavior, I will discuss two step process which reduced the re-render time by more than 50%.

Performance before any improvement

First Cycle

In the above image, I tried to dig deeper and analysed each function and time it consumes. You can do the same by clicking on Bottom-Up tab in Performance dev tool. It shows the time taken by different activities with the file name. First column Self Time represents the aggregated time spent directly in that activity, across all of its occurrences. Second column Total Time represents the aggregated time spent directly in that activity, across all of its occurrences. Clicking on the file name will take you to the function. This way you can locate the bottleneck in your application. I am able to find out the first issue by going through activities in descending order of their total time.

spread-operator

objectSpread is spread operator. Problem is styles for each date is calculated every time we hover over any date. There are 15 occurrences of spread operator in this file. It is always better to avoid premature optimization and aim to write clean, readable code. But in this case we found out that spread is actually causing the performance issue so we can replace spread with the native Object.assign method. And it helped. This change reduced re-render time from 165ms to 145ms 🎉

time reduced from 165ms to 145ms by replacing spread with Object.assign

Second Cycle

Now repeat the cycle mentioned above. Go through all the functions mentioned in Bottom-Up tab, and try to reduce the unnecessary calculations if any. In this case I found out that list of month year to show in drop down present in calendar header is calculated every time we hover over date. Calculation is loop over MIN_YEAR to MAX_YEAR, then for each year again loop over 12 months and create entries like January 2019, February 2019, ... Default MIN_YEAR is set to 2000 and MAX_YEAR is 2030, that is 31*12=372 calculations every time even when it is not required to do so. This is consuming around 40ms 😳. I memoized this calculation on MIN_YEAR and MAX_YEAR i.e. this calculations will be made again only if either of these value changes.

time reduced from 145ms to 95 by memoizing the calculations on MIN_YEAR and MAX_YEAR

This is big improvement from the point we started 🎊🎊. These are all the comparison in development mode. Here I would also like to present what we gain in production. This is the comparison when we are just hovering over the date randomly.

Before - Old Frame Drop

After - New Frame Drop

These changes helped me to get rid of majority of frame drop and lag that we are experiencing while selecting the date.

Conclusion

If you are experiencing performance issues in your application, this may help you to identify the potential issues. If you are using react, then react developer tools made it very easy to find the root cause. You can record the activity, see the different commits and if some component is rerendering then it also shows the prop or state change that caused it.

Subscribe for the newsletter