React.lazy() - Boost Mobile Performance
Tags: react, tutorial
Published: 2021-06-09 18:44:23

## The Problem
When creating new React applications they are set up for desktop being in mind when loading assets, not mobile connections. If we run a chrome **Lighthouse** report, we can see drastic differences between desktop performance and mobile performance.

As developers we want the same experience for our application regardless of whether you are on mobile or desktop.

Luckily this isn't that hard to implement!

### What Creates the Issue
When rendering new components in React we are importing javascript file and all imports that that component may contain!

The *flow* looks something like this:

![React file load](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tjie19kc6xcmmdci1u9a.png)
 
And the issue becomes a little clearer.

If we had a two files: `index.js` and `App.js`

```javascript
// index.js
import App from './App.js'

console.log('Hello World!')
```

```javascript
// App.js
import React, { Component } from 'react'
import SomeExtraClass from 'some-package'
import { func1, func2, func3 } from 'another-package'

console.log('Imported Packages!')
```

And ended up running `index.js`, we would see this output to our console:

```
/> "Imported Packages"
/> "Hello World!"
```

This is great because it brings all of our code into one useable scope for the file we imported it into, although the issue arises when we are importing code that isn't necessary at the time it is imported.

### Why Does It Impact Mobile?
As we build our applications, and as react is defined, larger screen sizes will be **rendering** more components to the page at a time than its *mobile* counterpart. 

This can lead to components rendering in the background and leads to technically decreased performance. *Due to the user waiting to load parts of the application they don't need or will see yet.*

I must note that this also impacts both *desktop* and *mobile* performance when we are building large applications with many nested components. The more we have to render to the end user, the longer the application will take to load.

So how do we go about only loading the necessary content for the page in prioritized order?

Welcome to [React.lazy](https://reactjs.org/docs/code-splitting.html#reactlazy).

### React.lazy

Luckily **React** has built a package that handles this for us: `React.lazy`.

We can import `lazy` as a named method from the *react* library if we would like or call to it as `React.lazy()`. 

> React.lazy takes a function that must call a dynamic import(). This must return a Promise which resolves to a module with a default export containing a React component.

Awesome, so we can now dynamically import extra components and leave it up to React to decide when to display them!

*React.lazy is currently not supported for server-side rendering.*

#### Code-Splitting With React.lazy
> Code-Splitting is a feature supported by bundlers like Webpack, Rollup and Browserify (via factor-bundle) which can create multiple bundles that can be dynamically loaded at runtime. - [reactjs.org](https://reactjs.org/docs/code-splitting.html)

This is what allows us to dynamically load separate files. Bundlers handle the different *flows* that our application will take when loading new files and creates separate bundle files that will be loaded for certain components.

This separation of concerns for runtime is what allows us to increase performance within our applications.

#### Suspense
Since this is loading our imports *asynchronously*, we don't want to wait for everything to load let's do something in the meantime for those couple of milliseconds it takes to import!

**Suspense** allows us to declare what should be displayed to the DOM while we wait on the rest of our components to load, until our promise resolves. Thanks `React.lazy`!

#### How To Use React.lazy
```javascript
// App.js
import React, { Component, lazy, Suspense } from 'react'
const Header = lazy( () => import('./Header.js'))
const Body = lazy( () => import('./Body.js'))

class App extends Component {

   render() {
      return (
         <div>
            <Suspense fallback={<p>Loading...</p>}>
               <p>Loaded Page!</p>
               <Header/>
               <Body/>
            </Suspense>
         </div>
      )
   }
}
```

As React goes to render our *App* component, the lifecycle now looks like this:
![React Lazy load](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v7e75g74vy5551wz2j9s.png)
 
### The Benefits
Now that we are asynchronously loading our extra components and have a *fallback* function, we no longer have to wait for components to render. As we can rely on the fallback function to handle our [first contentful paint](https://developer.mozilla.org/en-US/docs/Glossary/First_contentful_paint), and wait for them to load through `React.lazy` and then update our component!

This may only reduce loading times by a couple of milliseconds but it is better to display something like `Loading...` or maybe a [loading animation](https://loading.io/), rather than having a blank component and then allow React to come back in and update the component once its ready to render!

### Profiling With Lighthouse
To prove that `React.lazy` actually works let's profile it with Lighthouse using a basic application. You can find the example at [this repository](https://github.com/aj-rom/react-lazy-example) if you would like to test it on your own machine. 

#### Desktop
##### No Suspense
![desktop-noSuspense](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/urqj9fuouy60e6h53brn.png)
 
##### With Suspense
![desktop-suspense](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qitureplupmlexo4mcm2.png) 

#### Mobile
##### No Suspense
![mobile-noSuspense](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2sj4dkp5m06znkgis9ry.png)
 
#### Suspense 
![mobile-suspense](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/le0cbg6xjvmf4pctd51f.png)
 
## Conclusion
By using `React.lazy` and the `Suspense` component, we were able to drastically improve our websites loading performance by supplying a fallback render method. 

This will handle rendering until our component can actually be rendered to the user.

*Mobile* performance sees a much larger gain rather than *Desktop* performance, although both are increased!