Cardie is a simple application. All it does is display the profile information of a user. Commonly known as a user profile card.

The User Details Being Displayed

It also includes the added functionality of being able to change the user’s profession with a button click.

Cardie in Action :)

Upon clicking the button at the bottom of the app, the user’s profession is changed.

While you may be quick to laugh this off as a simple application, and nowhere near a real-world app, you may be surprised to know that the knowledge gained from hunting down performance issues with this example application applies to whatever real-world app you build.

So, stay calm and enjoy the article.

Here comes the checklist!

1. Identify wasted renders

Identifying wasted renders in your react applications is the perfect start to identifying most performance issues.

There are a couple different ways to approach this, but the simplest method is to toggle on the “highlight updates” option in the React dev tools.

How to enable the highlight update toggle in React devtools

While interacting with the app, updates are highlighted on the screen with a green flash.

The green flash shows the components in the app that are re-rendered by React under the hood.

With Cardie, upon changing the user’s profession, it appears that the entire parent component Appis re-rendered.

Note the green flash around the user card

That doesn’t look right.

The app works, but there’s really no reason for the entire component to be re-rendered when the change to be updated is in one small bit.

The actual update happens in a small part of the App.

A more ideal highlighted update should look like this:

Note how the highlighted update is contained within the small updated region.

In more complex applications, the impact of a wasted render may be huge! The re-rendered component may be large enough to promote performance concerns.

Having seen the problem, are there any solutions to this?

2. Extract frequently updated regions into isolated components

Once you’ve visually noted wasted renders in your application, a good place to start is to attempt to break up your component tree to support regions updated frequently.

Let me show you what I mean.

In Cardie, the App component is connected to the redux store via the connect function from react-redux. From the store, it receives the props: name, location, likes and description.

<App/> receives the props it needs directly from the redux store.

The description props define the current profession of the user.

Essentially, what’s happening is that whenever the user profession is changed by clicking the button, the description prop is changed. This change in props then causes the App component to be re-rendered entirely.

If you remember from React 101, whenever the props or state of a component changes, a re-render is triggered.

A React component renders a tree of elements. These elements are defined via props and state. If the props or state values changes, the tree of elements is re-rendered. This results in a new tree.

Instead of allowing the App component to re-render pointlessly, what if we localized the elements being updated to a specific React component?

For example, we could create a new component called Profession which renders its own DOM elements.

In this case, the Profession component will render the description of the user’s profession e.g “I am a Coder”.

The <Profession/> component will be rendered in the <App/> component.

The component tree would now look like this:

The <App/> component renders the elements it renders plus the <Profession/> component.

What’s also important to note here is that instead of letting <App/> worry about the profession prop, that’ll now be a concern for the <Profession/>component.

The profession will now be retrieved directly from the redux store by the <Profession/> component.

Whether you use redux or not, the point here is that App no longer has to be re-rendered owing to changes from the profession prop. Instead, the <Profession/> component will be.

Upon completing this refactor, we have an ideal update highlighted as seen below:

Note that the Description component takes in a profession prop. However, it passes on this prop to the Profession component. Technically, none of the other 3 components care about this profession prop.

The contents of these new components are super simple. For example, the <I /> component just returns a span element with an “I” text: span >I </span>

If the application runs, the result is just the same. The app works.

What’s interesting is that upon a change to the description prop, every child component of Profession is also re-rendered.

Once a new prop value is passed into the Description component, all the child components also re-render.

I added a few logs in the render methods of each child component - and as you can see they were indeed all re-rendered.

You may also view the highlighted updates using the react dev tools.

Note how there are green flashes around each of the words, I, am and a.

This behavior is expected. Whenever a component has either props or state changed, the tree of elements it renders is recomputed. This is synonymous to a re-render.

In this particular example, you’ll agree with me that if really makes no sense for <I/>, <Am/> and <A/> child components to be re-rendered. Yes, the props in the parent element, <Description/>changed, but if this were a sufficiently large application, this behaviour may pose some performance threats.

5. Use the production build

When deploying to production, always use the production build. This is a very simple, but great practice.

The “development build” warning you get with the react devtools in development.

If you have bootstrapped your application with create-react-app , to run the production build, use the command: npm run build.

This will yield bundle optimized files for production.

6. Employ code splitting

When you bundle your application, you likely have the entire application bundled in one large chunk.

The problem with this is that as your app grows, so does the bundle.

Once the user visits the site, he is sent a large chunk of code for the entire app.

Code splitting advocates that instead of sending this large chunk of code to the user at once, you may dynamically send chunks to the user when they need it.

A common example is with route based code splitting. In this method, the code is split into chunks based on the routes in the application.

The /home route gets a small chunk of code, so does the /about route.

Another approach is component based code splitting. In this method, if a component is currently not displayed to the user, it’s code may be delayed from being sent to the user.

Whichever method you stick to, it is important to understand the trade-offs and not degrade the user experience of your application.

Code splitting is great, and it can improve your application’s performance.

I have taken a conceptual approach to explain code splitting. If you want more technical grounds, please have a look at the official React docs. They do a decent job at explaining the concept technically.

Conclusion

Now you’ve got a decent checklist for tracking and fixing common performance issues in react apps. Go build some fast apps!

LogRocket is a frontend logging tool that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.

In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single page apps.