Dealing with callbacks as props in React
Tags: javascript, tutorial, react, eventhandlers
Published: 2019-02-17 15:58:43

---
title: Dealing with callbacks as props in React
published: true
description: React is a great library. It simplifies our work. We don't write all mutations ourselves, we describe result in render functions. Sometimes we have to render huge lists or optimize our code. In those case, we usually use PureComponent and memo. But, if we don't use callbacks correctly we can lose all profit from PureComponent. So, how to deal with callbacks and event listeners in React?
tags: javascript, tutorial, react, event handlers 
---


### TL;DR

1. Don't mix JSX and business-logic in one place, keep your code simple and understandable.
2. For small optimizations, you can cache function in class properties for classes or use the `useCallback` hook for function components. In this case, pure components won't be re-rendered every time when their parent gets re-rendered. Especially, callbacks caching is effective to avoid excess updating cycles when you pass functions as a prop to PureComponents.
3. Don't forget that event handler receives a synthetic event, not the original event. If you exit from the current function scope, you won't get access to synthetic event fields. If you want to get fields outside the function scope you can cache fields you need.

### Part 1. Event handlers, caching and code readability

React has quite a convenient way to add event handlers for DOM elements.
This is one of the first basic things that beginners face with.

```js
class MyComponent extends Component {
  render() {
    return <button onClick={() => console.log('Hello world!')}>Click me</button>;
  }
}
```

It's quite easy, isn't it? When you see this code, it isn't complicated to understand what will happen when a user clicks the button.
But what should we do, if the amount of the code in event handlers become more and more?
Let's assume, we want to load the list of developers, filter them `(user.team === 'search-team')` and sort using their age when the button was clicked:

```js
class MyComponent extends Component {
  constructor(props) {
    super(props);
    this.state = { users: [] };
  }
  render() {
    return (
      <div>
        <ul>
          {this.state.users.map(user => (
            <li>{user.name}</li>
          ))}
        </ul>
        <button
          onClick={() => {
            console.log('Hello world!');
            window
              .fetch('/usersList')
              .then(result => result.json())
              .then(data => {
                const users = data
                  .filter(user => user.team === 'search-team')
                  .sort((a, b) => {
                    if (a.age > b.age) {
                      return 1;
                    }
                    if (a.age < b.age) {
                      return -1;
                    }
                    return 0;
                  });
                this.setState({
                  users: users,
                });
              });
          }}
        >
          Load users
        </button>
      </div>
    );
  }
}
```

This code is so complicated. The business-logic part is mixed with JSX elements.
The simplest way to avoid it is to move function to class properties:

```js
class MyComponent extends Component {
  fetchUsers() {
    // Move business-logic code here
  }
  render() {
    return (
      <div>
        <ul>
          {this.state.users.map(user => (
            <li>{user.name}</li>
          ))}
        </ul>
        <button onClick={() => this.fetchUsers()}>Load users</button>
      </div>
    );
  }
}
```

We moved business-logic from JSX code to separated field in our class. The business-logic code needs to get access to `this`, so we made the callback as: `onClick={() => this.fetchUsers()}`

Besides it, we can declare `fetchUsers` class field as an arrow function:

```js
class MyComponent extends Component {
  fetchUsers = () => {
    // Move business-logic code here
  };
  render() {
    return (
      <div>
        <ul>
          {this.state.users.map(user => (
            <li>{user.name}</li>
          ))}
        </ul>
        <button onClick={this.fetchUsers}>Load users</button>
      </div>
    );
  }
}
```

It allows us to declare callback as `onClick={this.fetchUsers}`

What is the difference between them?

When we declare callback as `onClick={this.fetchUsers}` every render call will pass the same onClick reference to the `button`.
At the time, when we use `onClick={() => this.fetchUsers()}` each render call will init new function `() => this.fetchUsers()` and will pass it to the `button` onClick prop. It means, that `nextProp.onClick` and `prop.onClick` won't be equal and even if we use a PureComponent instead of `button` it will be re-rendered.

Which negative effects can we receive during the development?

In the vast majority of cases, we won't catch any visual performance issues, as Virtual DOM doesn't get any changes and nothing is re-rendered physically.
However, if we render big lists of components, we can catch lags on a big amount of data.

#### Why it's important to understand how functions are passed to the prop?

You can often find on Twitter or StackOverflow such advice:

"If you have issues with performance in React application, try to change inheritance in problem places from Component to PureComponent, or define shouldComponentUpdate to get rid of excess updating cycles".

If we define a component as a PureComponent, it means, that it has already the `shouldComponentUpdate` function, which implements shallowEqual between its `props` and `nextProps`.

If we set up new references as props to PureComponent in updating lifecycle, we'll lose all PureComponent advantages and optimizations.

Let's watch an example.
We implement Input component, that will show a counter representing the number of its updates

```js
class Input extends PureComponent {
  renderedCount = 0;
  render() {
    this.renderedCount++;
    return (
      <div>
        <input onChange={this.props.onChange} />
        <p>Input component was rerendered {this.renderedCount} times</p>
      </div>
    );
  }
}
```

Now we create two components, which will render the `Input` component:

```js
class A extends Component {
  state = { value: '' };
  onChange = e => {
    this.setState({ value: e.target.value });
  };
  render() {
    return (
      <div>
        <Input onChange={this.onChange} />
        <p>The value is: {this.state.value} </p>
      </div>
    );
  }
}
```

Second:

```js
class B extends Component {
  state = { value: '' };
  onChange(e) {
    this.setState({ value: e.target.value });
  }
  render() {
    return (
      <div>
        <Input onChange={e => this.onChange(e)} />
        <p>The value is: {this.state.value} </p>
      </div>
    );
  }
}
```

You can try the example here: https://codesandbox.io/s/2vwz6kjjkr
This example shows how we can lose all the advantages of PureComponents if we set the new references to the PureComponent every time in the render.

### Part 2. Using event handlers in function components

The new React hooks mechanism was announced in the new version of `React@16.8` (https://reactjs.org/docs/hooks-intro.html). It allows implementing full-featured function components, with full lifecycle built with hooks. You are able to change almost all class components to functions using this feature. (but it isn't necessary)

Let's rewrite `Input` Component from classes to functions.

Input should store the information about how many times it was re-rendered. With classes, we are able to use instance field via `this` keyword. But with functions, we can't declare a variable with `this`. React provides `useRef` hook which we can use to store the reference to the HtmlElement in DOM tree. Moreover `useRef` is handy to store any mutable data like instance fields in classes:

```js
import React, { useRef } from 'react';

export default function Input({ onChange }) {
  const componentRerenderedTimes = useRef(0);
  componentRerenderedTimes.current++;

  return (
    <>
      <input onChange={onChange} />
      <p>Input component was rerendered {componentRerenderedTimes.current} times</p>
    </>
  );
}
```

We created the component, but it isn't still `PureComponent`. We can add a library, that gives us a HOC to wrap component with PureComponent, but it's better to use the `memo` function, which has been already presented in React. It works faster and more effective:

```js
import React, { useRef, memo } from 'react';

export default memo(function Input({ onChange }) {
  const componentRerenderedTimes = useRef(0);
  componentRerenderedTimes.current++;

  return (
    <>
      <input onChange={onChange} />
      <p>Input component was rerendered {componentRerenderedTimes.current} times</p>
    </>
  );
});
```

Our `Input` component is ready. Now we'll rewrite A and B components.
We can rewrite the `B` component easily:

```js
import React, { useState } from 'react';
function B() {
  const [value, setValue] = useState('');

  return (
    <div>
      <Input onChange={e => setValue(e.target.value)} />
      <p>The value is: {value} </p>
    </div>
  );
}
```

We have used `useState` hook, which works with the component state. It receives the initial value of the state and returns the array with 2 items: the current state and the function to set the new state. You can call several `useState` hooks in the component, each of them will be responsible for its own part of the instance state.

How can we cache a callback? We aren't able to move it from component code, as it would be common for all different component instances.
For such kind of issues React has special hooks for caching and memoization. The handiest hook for us is `useCallback` https://reactjs.org/docs/hooks-reference.html

So, `A` component is:

```js
import React, { useState, useCallback } from 'react';
function A() {
  const [value, setValue] = useState('');

  const onChange = useCallback(e => setValue(e.target.value), []);

  return (
    <div>
      <Input onChange={onChange} />
      <p>The value is: {value} </p>
    </div>
  );
}
```

We cached function so that `Input` component won't be re-rendered every time when its parent re-renders.

**How does `useCallback` work?**

This hook returns the memoized version of the function. (that meant the reference won't be changed on every render call).
Beside the function which will be memoized, this hook receives a second argument. In our case, it was an empty array. 
The second argument allows passing to the hook the list of dependencies. If at least one of this fields gets changed, the hook will return a new version of the function with the new reference to enforce the correct work of your component.

The difference between inline callback and memoized callback you can see here: https://codesandbox.io/s/0y7wm3pp1w

**Why array of dependencies is needed?**

Let's suppose, we have to cache a function, which depends on some value via the closure:

```js
import React, { useCallback } from 'react';
import ReactDOM from 'react-dom';

import './styles.css';

function App({ a, text }) {
  const onClick = useCallback(e => alert(a), [
    /*a*/
  ]);

  return <button onClick={onClick}>{text}</button>;
}
const rootElement = document.getElementById('root');
ReactDOM.render(<App text={'Click me'} a={1} />, rootElement);
```

The component App depends on `a` prop. If we execute the example, everything will work correctly. However as we add to the end re-render, the behavior of our component will be incorrect:

```js
setTimeout(() => ReactDOM.render(<App text={'Next A'} a={2} />, rootElement), 5000);
```

When timeout executes, click the button will show `1` instead of `2`. It works so because we cached the function from the previous render, which made closure with previous `a` variable. The important thing here is when the parent gets re-rendered React will make a new props object instead of mutating existing one.
If we uncomment `/*a*/` our code will work correctly. When component re-renders the second time, React hook will check if data from deps have been changed and will return the new function (with a new reference).

You can try out this example here: https://codesandbox.io/s/6vo8jny1ln

React has a number of functions, which allow memoizing data: `useRef`, `useCallback` and `useMemo`.
The last one is similar to `useCallback`, but it is handy to memoize data instead of functions. `useRef` is good both to cache references to DOM elements and to work as an instance field.

At first glance, `useRef` hook can be used to cache functions. It's similar to instance field which stores methods. However, it isn't convenient to use for function memoization. If our memoized function uses closures and the value is changed between renders the function will work with the first one (that was cached). It means we have to change references to the memoized function manually or just use `useCallback` hook.

https://codesandbox.io/s/p70pprpvvx — here is the example with the right `useCallback` usage and wrong `useRef` one.

### Part 3. Synthetic events

We've already watched how to use event handlers, how to work with closures in callbacks but React also have differences in event objects inside event handlers.

Take a look at the `Input` component. It works synchronously. However, in some cases, you would like to implement debounce or throttling patterns. Debounce pattern is quite convenient for search fields, you enforce search when the user has stopped inputting symbols.

Let's create a component, which will call setState:

```js
function SearchInput() {
  const [value, setValue] = useState('');

  const timerHandler = useRef();

  return (
    <>
      <input
        defaultValue={value}
        onChange={e => {
          clearTimeout(timerHandler.current);
          timerHandler.current = setTimeout(() => {
            setValue(e.target.value);
          }, 300); // wait, if user is still writing his query
        }}
      />
      <p>Search value is {value}</p>
    </>
  );
}
```

This code won't work. React proxies events and after synchronous callback React cleanups the event object to reuse it in order to optimization. So our `onChange` callback receives Synthetic Event, that will be cleaned.

If we want to use `e.target.value` later, we have to cache it before the asynchronous code section:

```js
function SearchInput() {
  const [value, setValue] = useState('');

  const timerHandler = useRef();

  return (
    <>
      <input
        defaultValue={value}
        onChange={e => {
          clearTimeout(timerHandler.current);
          const pendingValue = e.target.value; // cached!
          timerHandler.current = setTimeout(() => {
            setValue(pendingValue);
          }, 300); // wait, if user is still writing his query
        }}
      />
      <p>Search value is {value}</p>
    </>
  );
}
```

Example: https://codesandbox.io/s/oj6p8opq0z

If you have to cache the whole event instance, you can call `event.persist()`. This function removes your Synthetic event instance from React event-pool. But in my own work, I've never faced with such a necessity.

### Conclusion:

React event handlers are pretty convenient as they

1. implement subscription and unsubscription automatically
2. simplify our code readability

Although there are some points which you should remember:

1. Callbacks redefinition in props
2. Synthetic events

Callbacks redefinition usually doesn't make the big influence on visual performance, as DOM isn't changed. But if you faced with performance issues, and now you are changing components to `Pure` or `memo` pay attention to callbacks memoization or you'll lose any profit from `PureComponents`. You can use instance fields for class components or `useCallback` hook for function components.