React Apps Can Be Incredibly Fast: Here’s How to Optimize Load Time
Do you want to increase the speed of your React app? This article will help you optimize load time and make your React App faster. Don't miss this guide.
Join the DZone community and get the full member experience.
Join For FreeWithout any doubt, speed is the most important factor for a positive user experience — that is, how much time your app takes to load and display content.
To give you an example, Walmart has reported a +2% conversion per one-second improvement of load time. As per Amazon, every 100ms load time results in a 1% loss in sales.
One effective solution to this problem is to optimize the app performance.
And when it comes to developing large-scale React apps, performance is the biggest issue developers have to face. More often than not, most of their efforts and thoughts go into how the app should function and end up looking.
So, are you struggling with the slow performance of your React app? Are you looking for ways to speed it up?
This article will cover some great tips that will save your React apps from straining and grunting.
Buckle up!
Is There Something Wrong With React?
There's nothing exactly wrong with React itself. With 35.9% of respondents preferring React.js as a web framework, it is slowly moving ahead to grab the top position of jQuery.
However, what troubles developers the most is the functional/reactive nature of this light and simple library.
Its main purpose is to help us render components that are compiled into HTML. React usually uses props from a parent to communicate between modules or classes. With one-way data binding (or unidirectional flow), it triggers the render method of all components every time the props are changed.
Sounds good?
Now the issue is this approach works just fine in simple and small apps. However, performance starts to deteriorate when the apps get bigger. If you add new features and functionalities, refreshing components takes more time, and loading time seems to be longer than before.
You might ask yourself, "Why? Where did I mess up?"
The answer is rendering.
The rendering components of React consume the most memory. But you can avoid unnecessary rendering if we properly plan your app structure.
Before we get into that, it's important for you to understand what "long loading time" actually means. Thankfully, the why-did-you-update library will help you know how often components re-render (excuse us for the jargon) in a specific view.
Complex Variables in React Render
shouldComponentUpdate
— you have probably heard of this method of React lifecycle. With this method, developers have control over the time components should update.
Surprising?
Let me tell you one thing: make sure to check your render method before using this method. And the solution is to prevent complex data declaration in render.
Let’s give you a simple example. Suppose you have two components: box and paragraph. Box consists of paragraphs and listens to prop "clicks." On the other hand, a paragraph contains an options prop with style and text.
So, a box is rendered every time "clicks" is changed.
Accordingly, a paragraph should not get refreshed.
Even if you block a paragraph from an irrelevant update, it will still be refreshed when the parent receives new props. And every time box render is triggered, it creates an options object, leading to your paragraph receiving a new prop as well.
Explaining in detail, the paragraph shouldComponentUpdate
function always provides us a new object in nextProps
parameter. Contrastingly, objects comparison in JavaScript depends on references
.
Hence, the nextProps
object points to a new prop even if you get a same-shaped object. The solution will help keep options object as a property of class or variable in such scenarios.
Although I gave you this object-based example, it’s the same with functions and arrays.
As you see, here I have written all the components as classes. In my personal opinion, I would go for stateless components due to their readable nature, which will help us prevent such context issues.
In this regard, recompose library comes to the rescue.
Recompose/PureComponent
As mentioned above, I needed to write all the paragraph components as classes and define the shouldComponentUpdate
function to avoid re-rendering.
Alternatively, we could do it in an easier way with recompose and its highly useful HOCs. Therefore, I suggest you use the PureComponent class (pure higher-order component) from React.
Here’s how you can write the example in that way:
After using PureComponent:
Additionally, you can visit recompose to explore other methods of this library.
Using React.Fragment
An efficient way to boost the performance of your React app is to use React.Fragment and prevent adding extra nodes to the DOM.
Working with React will require you to render different components or return a set of related elements.
For instance:
function App() {
return (
<h1>Hello React!</h1>
<h1>Hello React Again!</h1>
);
}
Running your React app with this code is most likely to end up in an error asserting that adjacent JSX elements must be wrapped in an enclosing tag.
This would require you to wrap both components within a parent div:
function App() {
return (
<div>
<h1>Hello React!</h1>
<h1>Hello React Again!</h1>
</div>
);
}
Although it will fix the problem, it comes with certain risks. You're unnecessarily adding an extra node to the DOM. This becomes a major issue as the above-motioned child component will be confined to a parent component.
function Table() {
return (
<table>
<td>This is a Table Component</td>
<Columns />
</table>
);
}
function Columns() {
return (
<div>
<td>Hello React!</td>
<td>Hello React Again!</td>
</div>
);
}
Because of added extra div, the final HTML for the Table component will be invalid.
function Table() {
return (
<table>
<td>This is a Table Component</td>
<div>
<td>Hello React!</td>
<td>Hello React Again!</td>
</div>
</table>
);
}
React fragments offers a better way to mitigate this error by not adding any extra node to the DOM.
Here’s how the syntax will look:
function Columns() {
return (
<React.Fragment>
<td>Hello React!</td>
<td>Hello React Again!</td>
</React.Fragment>
);
}
To declare a fragment, you may use the short syntax <></> as well:
function Columns() {
return (
<>
<td>Hello React!</td>
<td>Hello React Again!</td>
</>
);
}
Using React.Lazy and React.Suspense
If you want to speed up and optimize your app’s render time, lazy loading is a great way to do that. The main idea behind this approach is to load a component only when needed. React has multiple bundles of React.lazy API that can help in rendering a dynamic import as a regular component.
Here's the loading of your regular component:
import LazyComponent from './LazyComponent';
You can render the component using the lazy method to reduce the performance risk:
const LazyComponent = React.lazy(() => import('./LazyComponent'));
React.lazy
takes a callback function using the dynamic import()
. Next, it will return a promise that resolves to a module using a default export carrying a React component.
Now, within the Suspense component, this lazy component will be rendered. This will then enable you to include fallback content as a loading state as you wait for the loading of the lazy component.
import React, { Suspense } from 'react';
const LazyComponent = React.lazy(() => import('./LazyComponent'));
function MyComponent() {
return (
<div>
<Suspense fallback={<div>Loading....</div>}>
<LazyComponent />
</Suspense>
</div>
);
}
To Conclude
The React ecosystem is rich and extensive. There is a broad range of tools that we can leverage to develop large and complex apps. In this article, I have mentioned just a few tips and tricks to make your apps smooth and fast. All this effort is undoubtedly worth the benefits of creating a maintainable and high-performance codebase.
If there’s one thing you can take away from this article, it should be, at the least, the initiative to accelerate your app’s load time and boost your user experience. Thank you for reading!
Opinions expressed by DZone contributors are their own.
Comments