Why bind event handlers in React?

In JavaScript, methods are not bound to their classes. This means that methods of a class do not point to its instance. If a method needs this to refer to a class instance, we need to bind it. To demonstrate this let's change the context of a method.

Here is a simple class:

class Test {
  someMethod() {
    this.handleEvent();
  }
    
  handleEvent() {
    console.log('Hello');
  }
}

We can change a method's context by using call() which will give it a different this value.

const test = new Test();
const testTwo = {};
test.someMethod.call(testTwo);

Or we can use destructing assignment syntax available since ES6 release to pluck out someMethod and assign it to a const. By doing so we will create a new execution context.

const test = new Test();
const { someMethod } = test;
test.someMethod();

When running this code you should see:

Uncaught TypeError: this.handleEvent is not a function at Object.methodOne

Bind using bind()

The way to solve this is to bind someMethod() to the class. This has to be done in the constructor upon object initialisation.

class Test {
  constructor() {
    this.someMethod = this.someMethod.bind(this);
  }

  someMethod() {
    this.handleEvent();
  }

  handleEvent() {
    console.log('Hello');
  }
}

Now it does not really matter if create a new execution context or pass a different this value, the method will be bound to the class.

This is exactly why we have to bind event handlers in React class components.

import React, { Component } from 'react';

export default class SomeComponent extends Component {
  handleBtnEvent(e) {
    this.displayAlert(e.target.tagName);
  }

  displayAlert(tagName) {
    alert(`I have been clicked and I am a ${tagName}!`);
  }

  render() {
    return (
      <button onClick={this.handleBtnEvent}>Click me!</button>
    )
  }
}

This component will error and you will see the following type error message:

Uncaught TypeError: Cannot read property 'displayAlert' of undefined

Again we've got the same problem, this inside handleBtnEvent() method does not point to the instance of SomeComponent class. To solve this we are going to bind it to the class.

import React, { Component } from 'react';

export default class SomeComponent extends Component {
  constructor(props) {
    super(props);

    // bind handleBtnEvent to SomeComponent
    this.handleBtnEvent = this.handleBtnEvent.bind(this);
  }

  handleBtnEvent(e) {
    this.displayAlert(e.target.tagName);
  }

  displayAlert(tagName) {
    alert(`I have been clicked and I am a ${tagName}!`);
  }

  render() {
    return (
      <button onClick={this.handleBtnEvent}>Click me!</button>
    )
  }
}

The method is now bound to the class and can access displayAlert() method.

Bind using arrow function

We can also use arrow function in the callback to achieve the same result.

import React, { Component } from 'react';

export default class SomeComponent extends Component {    
  handleBtnEvent(e) {
    this.displayAlert(e.target.tagName);
  }

  displayAlert(tagName) {
    alert(`I have been clicked and I am a ${tagName}!`);
  }

  render() {
    return (
      <button onClick={(e) => this.handleBtnEvent(e)}>Click me!</button>
    )
  }
}

When using the arrow function, this gets automatically lexically bound to the context of its enclosing scope. It could either be a function or global scope. So in our case it is the scope of SomeComponent.

Final words

To summarise, whenever we pass event handlers as callbacks, the event handler method loses its context. The default binding gets set to undefined and the error gets thrown. To avoid this we need to bind the event handler to the component instance to explicitly set the context of this.