React performance - how to
Tags: react, performance
Published: 2022-03-30 23:09:53

In this post, we will go through some techniques and solutions to achieve good performances in your React application.

## Dynamic import

Your app doesn't need to be one big bundle because you don't need all parts of your application immediately.

If you build a website with multi-pages you need the current page to be loaded immediately and other later when the user request those.

```javascript
import { useState } from 'react';
import dynamic from 'next/dynamic';

const Modal = dynamic(() => import('../components/Modal'));

function Home() {
  const [showModal, setShowModal] = useState(false);

  return (
    <div>
      <button onClick={() => setShowModal(!showModal)}>Toggle modal</button>
      {showModal && <Modal />}
    </div>
  )
}

export default Home
```

[Next.js](https://nextjs.org/learn/foundations/how-nextjs-works/code-splitting) does this by default for you. It will create separate smaller bundles for each of your pages (routes).

Furthermore, you can dynamically load components and parts of the application which are not visible by default like modals or panels.

In the example above code for `Modal` will not be loaded until the component is rendered which means that your main bundle will be smaller and the initial page load faster. 

If you are not using Next.js the same thing you can achieve with [React.lazy](https://reactjs.org/docs/code-splitting.html). 
<br />

## React.memo

One thing that you don't want from your React app is unnecessary rerender 🙂.

If you wrap your component with `React.memo` you can ensure that your component will rerender only on props or state change, not whenever the component parent rerender.

`React.memo` **compares prev and next props** and if those are the same React will skip rendering the component, and reuse the last rendered result.
By default, props are compared shallowly but you can provide your custom comparison function as the second argument.

```javascript
function MyComponent(props) {
  ...
}
function areEqual(prevProps, nextProps) {
  /*
  return true if passing nextProps to render would return
  the same result as passing prevProps to render,
  otherwise return false
  */
}
export default React.memo(MyComponent, areEqual);
```

When to use `React.memo` is up to you, my recommendation is to use it when you have a problem with the performance and rerenders of your component is too expensive.
Also, you can use it by default for the components with a lot of elements, like the lists or the tables.

How to properly use `useCallback` with `React.memo` you can check in my previous post [here](https://dev.to/markoarsenal/are-you-using-usecallback-properly-5g2c).
<br />

## Profiler

Measure performances.

A great way to locate the components which are rerendering too many times or render slowly is to use `Profiler` HOC.
More about it you can read it [here](https://reactjs.org/docs/profiler.html).

For the component you want to measure performances you need to wrap it with `Profiler` component.
Props that you need to pass to the `Profiler` are `id` and `onRender`.

```javascript
return (
  <App>
    <Profiler id="Navigation" onRender={callback}>
      <Navigation {...props} />
    </Profiler>
    <Main {...props} />
  </App>
);
```

Also, you can have multiple `Profile` components at the same time and you can nest them to measure the performances of different components within the same subtree.

`onRender` callback provides the next, very useful, informations.

```javascript
function onRenderCallback(
  id, // the "id" prop of the Profiler tree that has just committed
  phase, // either "mount" (if the tree just mounted) or "update" (if it re-rendered)
  actualDuration, // time spent rendering the committed update
  baseDuration, // estimated time to render the entire subtree without memoization
  startTime, // when React began rendering this update
  commitTime, // when React committed this update
  interactions // the Set of interactions belonging to this update
) {
  ...
}
```

The most important information here is `actualDuration`, which shows how much time the component is needed for that current render.
Compare this time with `baseDuration` which is the time needed to render the component and entire subtree without memoization.
<br />

## useMemo

This hook can help you if you create an object or an array within your component and that creation is time expensive.

It accepts two parameters. The first one is the function that returns the value you want to memoize and the second one is an array of dependencies.
If any of the dependencies change `useMemo` will recalculate the value, otherwise will return memoized value.

```javascript
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
```

**When to use it?**
Well, I think that you can recognize operations that are expensive and can be memoized.
For example, if you have the `map` function within another `map` function and you are working with long arrays, that will be slow and it's good to be memoized.
Off course, you can measure how much time is needed for a specific operation and decide based on that.
For this purpose [performance.now()](https://developer.mozilla.org/en-US/docs/Web/API/Performance/now) can be used.
<br />

## [react-window](https://github.com/bvaughn/react-window)

`React` is not very performant when it comes to rendering large lists or grids.

To resolve this problem plugins like [react-window](https://github.com/bvaughn/react-window) can be used.
**The strategy is to only render the number of items that are in the viewport.**

From the documentation:
 - _It reduces the amount of work (and time) required to render the initial view and to process updates._
 - _It reduces the memory footprint by avoiding the over-allocation of DOM nodes._

```javascript
import { FixedSizeList as List } from 'react-window';
 
const Row = ({ index, style }) => (
  <div style={style}>Row {index}</div>
);
 
const Example = () => (
  <List
    height={150}
    itemCount={1000}
    itemSize={35}
    width={300}
  >
    {Row}
  </List>
);
```
&nbsp;

## Good practices

**Debounce function.**
It's not directly related to `React` but it can be applied.

If you call a function on an event that often occurs it's a good practice to debounce it.

You can use the `debounce` function from some library like [Lodash](https://lodash.com/docs/#debounce) or create your own.

```javascript
function debounce(func, timeout = 250){
  let timer;

  return (...args) => {
    clearTimeout(timer);
    timer = setTimeout(() => { func.apply(this, args); }, timeout);
  };
}
```

Now, for example, if you need to do something on `window resize` or `scroll`, it can be written like this:

```javascript
useEffect(() => {
  const onResize = debounce(function() {
    // The function's code
  }, 250);
  const onScroll = debounce(function() {
    // The function's code
  }, 250);

  window.addEventListener('resize', onResize);
  window.addEventListener('scroll', onScroll);

  return () => {
    window.removeEventListener('resize', onResize);
    window.removeEventListener('scroll', onScroll);
  }
});
```
&nbsp;

**Think how you organize your components.**

For example, if you have this component:

```javascript
const ItemsList = ({ items }) => {
  const [inputValue, setInputValue] = useState('');

  return (
    <div>
      <input type="text" value={inputValue} onChange={(e) => setInputValue(e.target.value)} />
      {items.map((item) => {
        ...
      })}
    </div>
  )
}
```

The problem with this is that the entire component will rerender on every input change which is not optimal because besides the input there is the list of items as well which stays unchanged.

A better approach would be to move `input` out of the component and wrap the `ItemsList` component with `React.memo` so it can depend only on the `items` prop.

```javascript
const ItemsList = React.memo(({ items }) => {
  return (
    <div>
      <input type="text" value={inputValue} onChange={(e) => setInputValue(e.target.value)} />
      {items.map((item) => {
        ...
      })}
    </div>
  )
})

const ParentComponent = () => {
  const [inputValue, setInputValue] = useState('');
  const [items, setItems] = useState([...]);

  return (
    <div>
      <input type="text" value={inputValue} onChange={(e) => setInputValue(e.target.value)} />
      <ItemsList items={items} />
    </div>
  )
}
```
&nbsp;

That's all, have fun and create performant `React` apps. 😉