React Application Performance

Based on my analysis, I have listed the below options to optimize the React Application performance.

  • Bundle Size

  • Unwanted Rendering

  • Performing intensive operation

  • Network Requests (Dns-prefetch or preconnect)

  • Load External Scripts

Running tools like Lighthouse on your application, gives you the performance metrics.

Bundle Size

Bundle size is the page size that gets loaded into the browser. Smaller the bundle size, page will be loaded faster. Optimum size of the bundle recommended is 244 KB.

Bundling is the process of the merging all imported files into a single file, which is then loaded into the browser. There are different tools to perform this process. I will use webpack for my explanations.

How do we know that what’s inside the bundle?

Add BundleAnalyzerPlugin into your webpack config which will give you the treemap visualization of all files available inside the bundle.

It helps us to find out which modules are occupying more space in the bundle and provide us the first way to optimize the page loading time.

It will be good to analyze the bundle whenever we add in a new package.

What to do if my bundle size is increasing over the time period?

By continuosly adding the new features to the application or by adding external dependencies, there is a good chance that our application size will get increased which will affect the loading time.

In this case,

  • Find the alternative package if it is thrid party. E.g. instead of using moment.js, try alternatives like date-fns

  • If it internal package, analyze the package or speak to the respective team.

  • Perform code splitting

In code-splitting, instead of the loading the entire application at a time, we shall load only what is required for the Home screen and parallely load the subsequent modules in the background or lazily load the applicatio, whenever it is requested.

How to load the modules in the background?

webpack 4.6.0+ adds support for prefetching and preloading.

How to lazily load the module?

React.lazy and suspense shall be used for lazily loading the component whenever it is requested. When webpack encounter this statement, it will create a separate bundle and it will not be included as part of main bundle.

Unwanted Rendering

One of the main feature of using React is the partial rendering of the application. It renders only the changed node in the application tree. But as a developer, we need to make sure we program correctly such that only the newly added or changed nodes only re-renders in the application. One key aspect of this rendering is that, it all happen in the background without a page refresh. So we are not aware of all the performance impacts of unwanted rendering happening in the background.

Using callback function in child component 

With pure component

Say you have a pure component that takes `onClick` callback in the props. Whenever there is a change in the parent component, child component gets re-rendered because the props passed into the child component is getting new references all the time.

React pure component looks only for references. In order to avoid re-rendering the child components, we need to avoid creating new references to the props passed into child components. This can be achieved using React.useCallback function.

useCallback will return a memoized version of the callback that only changes if one of the dependencies has changed.

Instead of passing the callback like below in the parent component, 

1 <SaveDialog onClick={() => setIsOpen(true)} />

use it with useCallback like below,

1 2 const showDialog = useCallback(() => setIsOpen(true), []); <SaveDialog onClick={showDialog} />

With functional component

Previous example works perfectly with React.PureComponent because basically pure component looks only references. But functional components gets re-rendered when parent component re-renders.

To avoid this, wrap the functional componet with React.memo function. React.memo works exactly the same way the pure component works. It also checks only the references passed into the props.

React.memo also provides the second argument where we can provide custom comparer to decide the component re-rendering.

Performing intensive operation

One common thing in all use cases is, we have restricted component rendering based on its references by making use of pure component or react memo function or using life cycle method. 

But there are few cases where component execute for longer time whenever there is a change in the state. If it is due to intensive calculation made in the component, react has come up with new caching technique called React.useMemo. 

This works similar to React.useCallback but it caches the value returned by the callback passed in the argument where as React.useCallback executes the callback everytime. Similar to React.memo, useMemo also takes second argument to set the callback function that evaluates when to invalidate the cache.

Establish early connection to the resource domain

When we request a page, first thing happens is, it performs DNS lookup, TLS negotiation, and the TCP handshake. All these process will take around 300 ms and it might vary.

In order to minimise these, we shall inform the browser about the request domain and browser will make the necessary setup. When we request the actual resource, resources will load more quickly.

To inform the browser about the domain, use dns-prefetch and preconnect like below in the head tag.

1 2 <link rel="preconnect" href="https://example.com"> <link rel="dns-prefetch" href="https://example123.com">

Difference between dns-prefetch and preconnect is that dns-prefetch will just do dns lookup where as preconnect will do TLS negotiation, and the TCP handshake as well.

Loading External Scripts

Any javascript that we reference or have as part of the bundle that needs to loaded, parsed and evaluated by the browser before the page gets rendered completely. All these operations will be performing synchronously.

Loading all external dependencies synchronously will take additional time. To minimise those, we shall use defer and async in the script tag. This will asynchronously loads the script by not blocking the page rendering in the browser.

Comments