8 React Performance Tricks: Make your app blazing fast!
Tags: react, javascript
Published: 2020-04-08 16:32:19

Optimization is one of the most important aspects of every developer, especially when building web apps. With the use of a virtual DOM, React makes UI updates as efficient as they can be.

**How React works**
Each React application is composed of many components in a tree formation. Components are functions that render the UI based on the props they receive. Whenever there is a change in data, React computer the differences between the current and the new UI and then proceeds to only apply UI changes to the real UI on the browser. The repeated comparing and rendering components can be one of the main causes of React performance issues.

**We want React to only re-render the components that are affected by the change in the data they receive (state or props). In this article I will show you 8 different techniques to improve the app's overall performance. Let's get to work!**

1. Avoid using index as key
2. UseEffect() and UseCallback()
3. React.Memo
4. React.Fragments
5. Lazy loading
6. Progressive images
7. JS Animations Instead of CSS Animations
8. Production build

<hr/>

# 0. Setup

Let's start by creating a basic React functional component that fetches data from an API with axios and displays the list to the UI. Our state keeps track of loading, errors and the dataU coming in. By combining useEffect with useCallback we make sure our API fetch is not called again on each render.

For the API I will pick a random fun public API, the Cocktail Database API. [Here](https://apilist.fun/) you can find a list of free public API's.

{% codesandbox objective-sun-wgfv3 %}

Create a new React app `npx create-react-app perf-test` and load up the code above.

**Measuring performance**
We will use the Chrome Performance Tab to measure the performance of our React app, which is what [React suggests](https://reactjs.org/docs/optimizing-performance.html#profiling-components-with-the-chrome-performance-tab). Make sure to disable all Chrome extensions, especially React DevTools. As they can significantly skew the results. I'm also throttling my CPU to 6x slowdown to reproduce a bigger amount of data and slower machine.

<hr/>

# 1. Avoid using index as key

The example I've created above fetches a list of 25 cocktail recipes, and gives the user the possibility to add their own recipe to the list. 

The addCocktail() function updates our state hook of cocktails when a user adds a new cocktail. With useRef() we can refer to the input fields and make sure they are not empty.

The problem in this example is that the component re-renders completely everytime we add a new recipe. When you enable paint flashing in Chrome Devtools, you can see which DOM nodes gets updated.

![img](https://i.ibb.co/YW7D4vN/flash1.png)

Render time: 336ms

This is because every cocktail in our array has been pushed one index to the right. A great improvement would be to use unique ID's instead of the indexes. You can use the npm package uuid to generate unique ID's.

```javascript
...
const updatedCocktails = [
        {
          idDrink: uuidv4(),
          strDrink: currentName,
          strInstructions: currentDescription
        }
      ].concat(cocktails);
...
cocktails.map((cocktail, index) => {
          const { idDrink, strDrink, strInstructions } = cocktail;
          return (
            <div key={idDrink}>
              <strong>{strDrink}</strong> - {strInstructions}
            </div>
          );
        })
...
```

Render time: 233ms

Awesome! Let's continue.

<hr/>

# 2. useEffect() and useCallback()

We are using the useEffect() hook to fetch the cocktails as soon as the component has mounted. It will only re-run when the dependency changes (, it this case the getCocktails function. With useCallback() we are making sure not to fetch the API data every time our App component re-renders.

In our example this won't make such a big difference, but when you have a huge component with many children, it can make a big difference to not re-render the component completely when, in this case, getCocktails changes the state or props of the parent component.

```javascript
function App() {

const getCocktails = useCallback((query) => {
    axios
      .get(`https://www.thecocktaildb.com/api/json/v1/1/search.php?f=${query}`)
      .then((response) => {
        setCocktails(response.data.drinks);
        setIsLoading(false);
      })
      .catch((error) => {
        setErrors(error);
      });
  }, []);

  useEffect(() => {
    getCocktails("a");
  }, [getCocktails]);

}
```

In the code above, the effect will re-run whenever `getCocktails` changes to make sure it has the latest version of `getCocktails`. The `getCocktails` function will be re-created everytime `App` re-builds without using the `useCallback` function and will call for an infinite loop when it changes the state or props from `App`. 

`useCallback` helps you prevent this by wrapping it around a function declaration and defining the dependencies of the function, it ensures that the function is only re-created if its dependencies changed. Hence the function is not re-built on every render cycle anymore.


# 3. Memoize React Components

React.Memo is a Higher Order Component (HOC) that wraps around another component by memoizing the result, which means that that React will skip rendering the component, and reuse the last rendered result. This can give your app a performance boost.

We can store our cocktail div in its own stateless functional component and wrap it with React.Memo().

```javascript
// index.js
...
cocktails.map(({ idDrink, ...otherProps }) => (<Cocktail key={idDrink} {...otherProps} />))
...
```

```javascript
// Cocktail.js
import React from "react";

const Cocktail = ({ strDrink, strInstructions }) => {
  return (
    <div>
      <strong>{strDrink}</strong> - {strInstructions}
    </div>
  );
};

export default React.memo(Cocktail);
```

Render time: 192ms

<hr/>

# 4. React.Fragments

It is common to have multiple components within a component in React. You always need to wrap your children into 1 main component. With Fragments, you can avoid adding more DOM nodes for your main wrapper component. You can use the `<Fragment>` tag and import it from React, or use empty tags `<></>`

Example:

```javascript
return (
    <>
      <h2>Cocktails</h2>
      {!isLoading ? (
        cocktails.map(({ idDrink, ...otherProps }) => (
          <Cocktail key={idDrink} {...otherProps} />
        ))
      ) : (
        <p>Loading...</p>
      )}
    </>
  );
```

In our example the difference is minimal, but if you have hundreds of thousands of components which are using div's it can make a big difference in performance.

<hr/>

# 5. Lazy loading

Another native method from React is the React.lazy function, which will load the requested component as soon as the current component has renderered.

For example:

```javascript
// Normal
import Home from '../screens/Home';
// Lazy
const Home = lazy(() => import("../screens/Home"));
``` 

The lazy component has to be called within a `<Suspense>` component so that the user sees a fallback item while the component is loading.

```javascript
<Suspense fallback={<Fragment>Loading...</Fragment>}>
   <Switch>
      <Route exact path="/" component={Home} />
   </Switch>
</Suspense>
```

<hr/>

# 6. Progressive image loading

Ever seen the images on Medium.com blurry while they were loading? They are using progressive image loading, which basically means you show a lower quality version image while the high res image is loading.

The [react-progressive-image](https://www.npmjs.com/package/react-progressive-image) package is a great way to integrate this into your app.

```javascript
...
import ProgressiveImage from "react-progressive-graceful-image";
import ProfileImgLarge from "../assets/img/profile-large.jpg";
import ProfileImgPlaceholder from "../assets/img/profile-placeholder.jpg";
...
<ProgressiveImage
   src={ProfileImgLarge}
   placeholder={ProfileImgPlaceholder}
>
   {(src) => (
      <ProfileImage src={src} alt="Profile of Sander de Bruijn" />
   )}
</ProgressiveImage>
...
```

Using this technique you can show your users images directly by using e.g. <10kb images as placeholders.

<hr/>

# 7. JS Animations instead of CSS animations.

Many developers actually think that CSS animations are more performed than JS animation, but [this article](https://css-tricks.com/myth-busting-css-animations-vs-javascript/) shows the opposite when using complex animations. Besides that, JavaScript-based animation delivers far more flexibility, better workflow for complex animations and rich interactivity.

For simple animations, CSS works just fine. But for more complex ones, I would recommend using the GSAP library.

# 8. Production build

This has the biggest impact of all. On development, React servers tons of add-ons to make our life easier. However, users do not need these add-ons. By executing `yarn build` (or npm build) webpack builds the output folder for us when using create-react-app.

Render time: <60ms

<hr />

That is it! Hope you learned something from this tutorial. Make sure to follow me for more tips and tricks.