Tips to Improve React Apps performance
Has your React app been feeling kinda sluggish? Are you afraid of turning on the “paint flash” in Chrome DevTools because of what you might see? Try out these 5 performance tips!
This article contain 5 performance tips for React development. You can use this table of contents to navigate quickly around this article.
Use memo and PureComponent
Consider this simplistic React app below. Do you think that ComponentB will re-render if only props.propA changes value?
The answer is YES! This is because MyApp is actually re-evaluated (or re-rendered 😏) and ComponentB is in there. So even though its own props didn’t change, it’s parent component causes it to re-render.
This concept also applies to render methods for Class-based React components.
The authors of React perceived this wouldn’t always be the desired result, and there would be some easy performance gains by simply comparing old and new props before re-rendering… this is essentially what React.memo and React.PureComponent are designed to do!
Let’s use memo with functional components, (we’ll look at Class-based components after):
That’s it! You just need to wrap ComponentB with a memo() function. Now it will only re-render when propB actually changes value regardless of how many times its parent re-renders!
Let’s look at PureComponent. It’s essentially equivalent to memo, but it’s for class-based components.
These performance gains are almost too easy! You might wonder why React components don’t automatically include these internal safeguards against excessive re-rendering.
There’s actually a hidden cost with memo and PureComponent. Since these helpers compare old/new props, this can actually be its own performance bottlenecks. For example, if your props are very large, or you’re passing React components as props, the comparison of old/new props can be costly.
Silver bullets in the world of programming are rare! And memo/PureComponent aren’t an exception. You’ll definitely want to test drive them in a measured, thoughtful way. In some cases, they can surprise you how much computational savings they can yield.
For React Hooks, check out useMemo as a similar way to prevent unnecessary computational work
Avoid Anonymous Functions
Functions that are inside the main body of a component are usually event handlers, or callbacks. In many cases you might be tempted to use anonymous functions for them:
useCallback is another way to avoid the pitfalls of anonymous functions, but it has similar tradeoffs that accompany React.memo that we covered earlier.
With class-based components, the solution is pretty easy and doesn’t really have any downsides. It’s the recommended way to define handlers in React:
Avoid Object Literals
This performance tip is similar to the previous section about anonymous functions. Object literals don’t have a persistent memory space, so your component will need to allocate a new location in memory whenever the component re-renders:
Each time ComponentA is re-rendered a new object literal has to be “created” in-memory. Additionally, this also means that ComponentB is actually receiving a different style object. Using memo and PureComponent won’t even prevent re-renders here 😭
This tip doesn’t apply to style props only, but it’s typically where object literals are unwittingly used in React components.
This can be easily fixed by naming the object (outside of the component’s body of course!):
Use React.lazy and React.Suspense
Part of making your React app fast can be accomplished via code-splitting. This feature was introduced to React v16 with React.lazy and React.Suspense.
– bundle.js (10MB!)
Using code-splitting, this could cause the initial network request for the bundle to be significantly smaller:
– bundle-1.js (5MB)
– bundle-2.js (3MB)
– bundle-3.js (2MB)
The initial network request will “only” need to download 5MB, and it can start showing something interesting to the end user. Imagine a blog website that only needs the header, and footer initially. Once that’s loaded it’ll begin to request the 2nd bundle that contains the actual blog posts. This is a just rudimentary example where code-splitting would be handy. 👏👏👏
How code splitting this done in React?
If you’re using Create React App, it’s already configured for code-splitting, so you can simply use React.lazy and React.Suspense out of the gate! If you’re configuring webpack yourself it should look like this.
Here’s a simple example where lazy and Suspense is implemented:
Notice the fallback prop. This will be shown to the user immediately while the 2nd bundle chunk is loaded (eg., BlogPosts).
Check out this great article on Code Splitting with React Suspense 🐊
Avoid Frequent Mounting/Unmounting
Many times we’re used to making components disappear using a ternary statement (or something similar):
Since DropdownItems is removed from the DOM it can cause a repaint/reflow by the browser. These can be expensive, especially if it causes other HTML elements to shift around.
In order to mitigate this, it’s advisable to avoid completely unmounting components. Instead, you can use certain strategies like setting the CSS opacity to zero, or setting CSS visibility to “none”. This will keep the component in the DOM, while making it effectively disappear without incurring any performance costs.