Cache your React event listeners to improve performance.
Tags: javascript, reactnative, react, webdev
Published: 2019-06-02 20:41:40


An under-appreciated concept in JavaScript is how objects and functions are _references_, and that directly impacts React performance. If you were to create two functions that are completely identical, they are still not equal. Try for yourself:

```javascript
const functionOne = function() { alert('Hello world!'); };
const functionTwo = function() { alert('Hello world!'); };
functionOne === functionTwo; // false
```

But check out the difference if you assign a variable to an already-existing function:

```javascript
const functionThree = function() { alert('Hello world!'); };
const functionFour = functionThree;
functionThree === functionFour; // true
```

Objects work the same way.

```javascript
const object1 = {};
const object2 = {};
const object3 = object1;
object1 === object2; // false
object1 === object3; // true
```

If you have experience in other languages, you may be familiar with _pointers_. What is happening here is that each time you create an object, you are allocating some amount of memory on the device. When I said that `object1 = {}`, I have created a chunk of bytes in the user’s RAM that is dedicated specifically to `object1`. It is fair to imagine `object1` as an address that contains where in RAM its key-value pairs are located. When I said `object2 = {}`, I created a _different_ chunk of bytes in the user’s RAM that is dedicated specifically to `object2`. Does the _address_ of `object1` match the address of `object2`? No. That is why the equality check for the two variables does not pass. Their key-value pairs may be exactly the same, but their addresses in memory are different, and that is what is being compared.

When I assigned `object3 = object1`, I am assigning the value of `object3` to be the address of `object1`. It is not a _new_ object. It is that same location in memory. You can verify this like so:

```javascript
const object1 = { x: true };
const object3 = object1;
object3.x = false;
object1.x; // false
```

In this example, I created an object in memory and assigned it to `object1`. I then assigned `object3` to that same address in memory. By mutating `object3`, I have changed the value at that location in memory, meaning _all other references to that location in memory change as well_. `object1`, which still points to that location in memory, now has a changed value.

This is a very common error for junior developers to make, and likely warrants an in-depth tutorial of its own; but this particular tutorial is about React performance, which may be compromised even by developers with more seniority who have simply not considered the implications of variable references.

What does this have to do with React? React has an intelligent way of saving processing time to boost performance: If a PureComponent’s props and state have not changed, then the output of `render` must not have changed either. Clearly, if all things are equal, nothing has changed. If nothing has changed, `render` must return the same output, so let’s not bother executing it. This is what makes React _fast_. It only renders as needed.

React determines if its props and state are equal the same way JavaScript does — by simply comparing them with the `==` operator. React _does not_ shallow or deep compare objects to determine if they are equal. Shallow comparison is a term used to describe comparing each key-value pair of an object, as opposed to comparing the memory address. Deep comparison is going one step further and, if any of the values in the key-value pair are also objects, comparing those key-value pairs as well, ad nauseum. React does neither: it merely checks if the _references_ are the same.

If you were to change a component’s prop from `{ x: 1 }` to another object `{ x: 1 }`, React will re-render, because those two objects do not reference the same location in memory. If you were to change a component’s prop from `object1` (from above) to `object3`, React would _not_ re-render, because those two objects _are_ the same reference.

In JavaScript, functions are handled the same way. If React receives an identical function with a different memory address, it will re-render. If React receives the same function reference, it will not.

This is an unfortunately common scenario I come across during code review:

```javascript
class SomeComponent extends React.PureComponent {

  get instructions() {
    if (this.props.do) {
      return 'Click the button: ';
    }
    return 'Do NOT click the button: ';
  }

  render() {
    return (
      <div>
        {this.instructions}
        <Button onClick={() => alert('!')} />
      </div>
    );
  }
}
```

This is a pretty straightforward component. There’s a button, and when it is clicked, it alerts. Instructions tell you whether or not you should click it, which is controlled by the `do={true}` or `do={false}` prop of `SomeComponent`.

What happens here is that every time `SomeComponent` is re-rendered (such as `do` toggling from `true` to `false`), `Button` is re-rendered too! The `onClick` handler, despite being exactly the same, is being _created_ every `render` call. Each render, a new function is created (because it is created in the render function) in memory, a new reference to a new address in memory is passed to `<Button />`, and the `Button` component is re-rendered, despite absolutely nothing having changed in its output.

## The Fix

If your function does not depend on your component (no `this` contexts), you can define it outside of the component. All instances of your component will use the same function reference, since the function is identical in all cases.

```javascript
const createAlertBox = () => alert('!');

class SomeComponent extends React.PureComponent {

  get instructions() {
    if (this.props.do) {
      return 'Click the button: ';
    }
    return 'Do NOT click the button: ';
  }

  render() {
    return (
      <div>
        {this.instructions}
        <Button onClick={createAlertBox} />
      </div>
    );
  }
}
```

In contrast to the previous example, `createAlertBox` remains the same reference to the same location in memory during every `render`. `Button` therefore _never_ has to re-render.

While `Button` is likely a small, quick-to-render component, you may see these inline definitions on large, complex, slow-to-render components, and it can really bog down your React application. It is good practice to simply never define these functions inside the render method.

If your function _does_ depend on your component such that you cannot define it outside the component, you can pass a method of your component as the event handler:

```javascript
class SomeComponent extends React.PureComponent {

  createAlertBox = () => {
    alert(this.props.message);
  };

  get instructions() {
    if (this.props.do) {
      return 'Click the button: ';
    }
    return 'Do NOT click the button: ';
  }

  render() {
    return (
      <div>
        {this.instructions}
        <Button onClick={this.createAlertBox} />
      </div>
    );
  }
}
```

In this case, each instance of `SomeComponent` has a different alert box. The click event listener for `Button` needs to be unique to `SomeComponent`. By passing the `createAlertBox` method, it does not matter if `SomeComponent` re-renders. It doesn’t even matter if the `message` prop changes! The address in memory of `createAlertBox` does not change, meaning `Button` does not have to re-render, and you save processing time and improve rendering speed of your application.

_But what if my functions are dynamic?_

## The Fix (Advanced)

_**Author’s Note**_: I wrote the following examples off the top of my head as a way to repeatedly reference the same function in memory. These examples are meant to make comprehension of references easy. While I would recommend reading this section for the purpose of comprehending references, I have included a better implementation at the end that was generously contributed by Chris Ryan via comment. His solution takes into account cache invalidation and React’s built-in memory management.

There is a very common use case that you have a lot of unique, dynamic event listeners in a single component, such as when mapping an array.

```javascript
class SomeComponent extends React.PureComponent {
  render() {
    return (
      <ul>
        {this.props.list.map(listItem =>
          <li key={listItem.text}>
            <Button onClick={() => alert(listItem.text)} />
          </li>
        )}
      </ul>
    );
  }
}
```

In this case, you have a variable number of buttons, making a variable number of event listeners, each with a unique function that you cannot possibly know what is when creating your `SomeComponent`. How can you possible solve this conundrum?

Enter _memoization_, or what may be easier to refer to as simply, _caching_. For each unique value, create and cache a function; for all future references to that unique value, return the previously cached function.

This is how I would implement the above example.

```javascript
class SomeComponent extends React.PureComponent {

  // Each instance of SomeComponent has a cache of click handlers
  // that are unique to it.
  clickHandlers = {};

  // Generate and/or return a click handler,
  // given a unique identifier.
  getClickHandler(key) {

    // If no click handler exists for this unique identifier, create one.
    if (!Object.prototype.hasOwnProperty.call(this.clickHandlers, key)) {
      this.clickHandlers[key] = () => alert(key);
    }
    return this.clickHandlers[key];
  }

  render() {
    return (
      <ul>
        {this.props.list.map(listItem =>
          <li key={listItem.text}>
            <Button onClick={this.getClickHandler(listItem.text)} />
          </li>
        )}
      </ul>
    );
  }
}
```

Each item in the array is passed through the `getClickHandler` method. Said method will, the first time it is called with a value, _create_ a function unique to that value, then return it. All future calls to that method with that value will not create a new function; instead, it will return the reference to the previously created function in memory.

As a result, re-rendering `SomeComponent` will not cause `Button` to re-render. Similarly, adding items to the `list` prop will create event listeners for each button dynamically.

You may need to use your own cleverness for generating unique identifiers for each handler when they are determined by more than one variable, but it is not much harder than simply generating a unique `key` prop for each JSX object in the mapped result.

A word of warning for using the `index` as the identifier: You may get the wrong result if the list changes order or removes items. When your array changes from `[ 'soda', 'pizza' ]` to just `[ 'pizza' ]` and you have cached your event listener as `listeners[0] = () => alert('soda')`, you will find that when you users click the now-index-0 button for pizza that it alerts `soda`. This is the same reason React advises against using array indices for key props.

## A Better Implementation

![](https://thepracticaldev.s3.amazonaws.com/i/c0snwqrl5r77tcaz4f1j.png)

_Courtesy of Medium user Chris Ryan._

## Conclusion

If you liked this article, feel free to give it a heart or a unicorn. It’s quick, it’s easy, and it’s free! If you have any questions or relevant great advice, please leave them in the comments below.

To read more of my columns, you may follow me on [LinkedIn](https://www.linkedin.com/in/charles-stover), [Medium](https://medium.com/@Charles_Stover), and [Twitter](https://twitter.com/CharlesStover), or [check out my portfolio on CharlesStover.com](https://charlesstover.com/).