Warped Re-rendering | React performance optimization
Tags: react, hooks, optimization
Published: 2019-12-03 10:16:13

---
title: Warped Re-rendering | React performance optimization
published: true
description: Introducing a new approach to optimize your react app for isolating the re renders to the particular required subtree.
tags: reactjs, hooks, optimization
---

In this post, I will be introducing a new optimization that will significantly improve your React app performance. In one of my particular case it reduced the amount of _react commits_ from **~200 to just ~2** (You can visualize these in the new React Profiler :fire: :fire:). It's a very specific case, but it proves the utility of the approach and illustrate it's benefits. 

**Most importantly, we shouldn't be [_lifting the state up_](https://reactjs.org/docs/lifting-state-up.html) if we are doing that only to set state from another component**. Let's understand this by looking at a contrived example.

## The problem

I have a React app, where I have implemented a top level `<Loader />` component whose job is to either display the loading symbol or not. It looks something like this.

```jsx
import React, { useState } from "react";

const AppContext = React.createContext();

export default function App() {
  const [isVisible, setShowLoader] = useState(false);
  return (
    <AppContext.Provider value={{ setShowLoader }}>
      <div>
        {isVisible && <Loader />}
        Remainder of my app
      </div>
    </AppContext.Provider>
  );
}

```

In the above code, you can see that I have a Loader component at the top level, and I have passed it's setter down using the context. Now `setShowLoader` is used by various parts of my code to display the loader (primarily before API call) and hide the loader (post call is settled).

By now the problem with this approach is obvious; Since we have this state at the top level component, every time I call `setShowLoader` the entire App will go into reconciliation. And since most of us don't do pre optimization, this was re rendering my whole app.

## Introducing Mitt

We have a small utility that we have written in our codebase, which is basically a pub/sub model using which we can pass events & data anywhere to anywhere. We can use this to dispatch events from any component to any other component. Upon researching online, I found an excellent package that exists for this purpose.

```js
import mitt from 'mitt';
 
const emitter = mitt();
 
// listen to an event
emitter.on('foo', e => console.log('foo', e))
 
// listen to all events
emitter.on('*', (type, e) => console.log(type, e) )
 
// fire an event
emitter.emit('foo', { a: 'b' })
 
// working with handler references:
function onFoo() {}
emitter.on('foo', onFoo)   // listen
emitter.off('foo', onFoo)  // unlisten
```

Now with this utility I can communicate between any components in my codebase.

## The solution

Now that I know I can communicate from any part of my code to my top level Loader component, I can move my `isVisible` state into `<Loader />` component. With this, whenever I change my state, only my Loader component will re-render and my entire app re-render is prevented. My final code will look as follows.

```jsx
import React, { useState } from "react";
import mitt from 'mitt';

const AppContext = React.createContext();
const events = mitt();

export const showLoader = val => {
  events.emit("showLoader", val);
};

function Loader() {
  const [isVisible, setShowLoader] = useState(false);
  useEffect(() => {
    events.on("showLoader", setShowLoader);
    return () => {
      events.off("showLoader", setShowLoader);
    };
  }, []);
  if (isVisible) {
    return <div>Loading GIF</div>;
  }
  return null;
}

export default function App() {
  return (
    <AppContext.Provider value={{ showLoader }}>
      <div>
        <Loader />
        Remainder of my app
      </div>
    </AppContext.Provider>
  );
}
```

## To summarize

* **We can use this whenever we have situation where the state is used in one component (or it's subtree) but is updated from other places in the code**
* We shouldn't be [_lifting the state up_](https://reactjs.org/docs/lifting-state-up.html) if we are doing that **only to** set state from another component.
* We have depended on a pub/sub model to communicate between component. https://github.com/developit/mitt
* By moving the state of the `Loader` to the Loader component itself, we have **avoided re-rendering the entire app**.

> Also Note: Since I have exported the `showLoader` function from the App, I can import it into any component and use it; instead of taking it from Context.