JIYIK CN >

Current Location:Home > Learning > WEB FRONT-END > React >

Why do you need to bind event handlers in React Class Components?

Author:JIYIK Last Updated:2025/03/15 Views:

When using React, we must have come across control components and event handlers. We need to use in the constructor of the custom component .bind()to bind these methods to the component instance. As shown in the following code:

class Foo extends React.Component{
  constructor( props ){
    super( props );
    this.handleClick = this.handleClick.bind(this);
  }
  
  handleClick(event){
    // 事件处理程序
  }
  
  render(){
    return (
      <button type="button" 
      onClick={this.handleClick}>
      Click Me
      </button>
    );
  }
}

ReactDOM.render(
  <Foo />,
  document.getElementById("app")
);

In this article, we'll explain why we need to do this.

Javascript or React

First of all we have Javascript to blame and not React. This is not something we need to do because of the way React works or JSX. It's because of the way this binding works in JavaScript.

Let's see what happens if we don't bind the event handler method to its component instance:

class Foo extends React.Component{
  constructor( props ){
    super( props );
  }
    
  handleClick(event){
    console.log(this); // 'this' 结果为 undefined
  }
    
  render(){
    return (
      <button type="button" onClick={this.handleClick}>
        Click Me
      </button>
    );
  }
}

ReactDOM.render(
  <Foo />,
  document.getElementById("app")
);

If we run this code, we will see that the value of this printed out inside the event handler method is undefined . The handleClick() method seems to have lost either its context (component instance) or this value.

How "this" binding works in JavaScript

As mentioned, this happens because of the way this binding works in JavaScript. I won’t go into detail in this post, but we will cover how this binding works in JavaScript in a subsequent post.

But what is relevant to our discussion here is that the value of this inside a function depends on how the function is called.

Default Binding

function display(){
 console.log(this); // 'this' 只想全局对象 object
}

display(); 

This is a normal function call. In this case, the value of this in the display() method is either windowor the global object in non-strict mode. In strict mode, the value of this is undefined.

Implicit Binding

var obj = {
 name: '迹忆客',
 display: function(){
   console.log(this.name); // 'this' 指向 obj
  }
};

obj.display(); // 迹忆客 

When we call the function this way—with a context object in front of it—the value of this in display() is set to obj.

But when we assign this function reference to some other variable and call the function using this new function reference, we get a different this value in display().

var name = "uh oh! global";
var outerDisplay = obj.display;
outerDisplay(); // uh oh! global

In the example above, when we called outerDisplay(), we did not specify a context object. This is a normal function call with no owner object. In this case, the value of this inside display() falls back to the default binding. If the called function uses strict mode, it points to the global object or undefined.

This is especially true when passing a function such as a callback to another custom function, a third-party library function, or a built-in JavaScript function such as setTimeout.

Consider the following setTimeout definition and then calling it.

// setTimeout 的模拟实现
function setTimeout(callback, delay){

   //等待 'delay' 毫秒
   callback();
   
}

setTimeout( obj.display, 1000 );

We can see that when we call setTimeout, JavaScript internally assigns obj.display to its parameter callback.

callback = obj.display;

As we saw earlier, this assignment causes the display() function to lose its context. When this callback function is eventually called in the setTimeout, the this value in display() falls back to the default binding.

var name = "uh oh! global";
setTimeout( obj.display, 1000 );

// uh oh! global

Explicit Binding

To avoid this, we can use the bind() method to explicitly bind the this value to a function.

var name = "uh oh! global";
obj.display = obj.display.bind(obj); 
var outerDisplay = obj.display;
outerDisplay();

// 迹忆客

Now, when we call outerDisplay(), the value of this refers to obj inside display().

Even though we pass obj.display as the callback, the this value in display() correctly refers to obj.

We saw this at the beginning of this article in a React component called Foo. If we don't bind an event handler with this, its value inside the event handler is set to undefined.

As mentioned and explained, this is because of the way this binding works in JavaScript, which has nothing to do with how React works. So let's remove the React-specific code and build a similar pure JavaScript example to simulate this behavior.

class Foo {
  constructor(name){
    this.name = name
  }
  
  display(){
    console.log(this.name);
  }
}

var foo = new Foo('Saurabh');
foo.display(); // Saurabh

// 下面的赋值操作模拟了上下文丢失,类似于在实际 React 组件中将处理程序作为回调传递
var display = foo.display; 
display(); // TypeError: this is undefined

Instead of simulating actual events and handlers, we use synonymous code. As we observed in the React component example, the this value is undefined because the context is lost after passing the handler as a callback — synonymous with the assignment operation. This is also what we observe in this non-React JavaScript snippet.

You might be asking: “Wait a minute! Shouldn’t this value refer to the global object since we are running this in non-strict mode according to the default binding rules?”

Here is the answer

The bodies of class declarations and class expressions are executed in strict mode, that is, constructors, static and prototype methods. Getter and setter functions are executed in strict mode.

Therefore, to prevent errors, we need to bind the this value like this:

class Foo {
  constructor(name){
    this.name = name
    this.display = this.display.bind(this);
  }
  
  display(){
    console.log(this.name);
  }
}

var foo = new Foo('迹忆客');
foo.display(); // 迹忆客

var display = foo.display;
display(); // 迹忆客

The results of running the above code are as follows

Javascript this binding example

We don't need to do this in the constructor, we can do it elsewhere as well. Consider this:

class Foo {
  constructor(name){
    this.name = name;
  }
  display(){
    console.log(this.name);
  }
}

var foo = new Foo('迹忆客');
foo.display = foo.display.bind(foo);
foo.display(); // 迹忆客

var display = foo.display;
display(); // 迹忆客

But the constructor is the best and most efficient place to write our event handler binding statements because this is where all the initialization happens.

Why don't we need to bind "this" for arrow functions?

We have two other ways to define event handlers in React components.

  • Public class field syntax (experimental)
    class Foo extends React.Component{
    handleClick = () => {
      console.log(this); 
    }
    render(){
      return (
        <button type="button" onClick={this.handleClick}>
          Click Me
        </button>
      );
    }
    } 
    ReactDOM.render(
    <Foo />,
    document.getElementById("app")
    );
    
  • Arrow functions in callbacks
    class Foo extends React.Component{
    handleClick(event){
      console.log(this);
    } 
    render(){
      return (
        <button type="button" onClick={(e) => this.handleClick(e)}>
          Click Me
        </button>
      );
    }
    }
    ReactDOM.render(
    <Foo />,
    document.getElementById("app")
    );
    

Both of these use arrow functions introduced in ES6. When using these alternatives, our event handler is already automatically bound to the component instance and we don’t need to bind it in the constructor.

The reason is that in case of arrow function, this is lexically bound. This means that it uses the context of the enclosing function (or global) scope as its this value.

In the case of the public class field syntax example, the arrow function is contained within the Foo class or constructor, so the context is the component instance, which is what we want.

In the case of arrow functions as callback examples, the arrow function is contained in the render() method, which is called by React in the context of the component instance. That’s why the arrow function also captures the same context and the this value inside it will correctly point to the component instance.

Summarize

In React's class component, when we pass an event handler reference as a callback like this

<button type="button" onClick={this.handleClick}>Click Me</button>

Event handler methods lose their implicitly bound context. When the event occurs and the handler is called, the this value falls back to the default binding and is set to undefined, as class declarations and prototype methods operate in strict mode.

When we bind the event handler's this to the component instance in the constructor, we can pass it as a callback without worrying about it losing its context.

Arrow functions are not affected by this behavior because they use lexical this binding which automatically binds them to the scope in which they are defined.

For reprinting, please send an email to 1244347461@qq.com for approval. After obtaining the author's consent, kindly include the source as a link.

Article URL:

Related Articles

React Tutorial - Transferring Props

Publish Date:2025/03/16 Views:185 Category:React

React transfers Props. Props are generated when components are encapsulated. Components expose some properties (Props) to the outside world to complete some functions.

React Tutorial: Props Anti-Pattern

Publish Date:2025/03/16 Views:183 Category:React

React's Props anti-pattern, using Props to generate state in getInitialState is an anti-pattern - Anti-Pattern.

React Tutorial - Props Validation

Publish Date:2025/03/16 Views:99 Category:React

Props validation is a very useful way to use components correctly. It can avoid many bugs and problems as your application becomes more and more complex. In addition, it can make your program more readable.

Rendered fewer hooks than expected error in React

Publish Date:2025/03/16 Views:50 Category:React

When we use a hook after a condition that may return a value, we get the error "Rendered fewer hooks than expected. This may be caused by an accidental early return statement". To fix this error, you need to move all React hooks to any condition that

Fix Uncaught ReferenceError: useState is not defined in React

Publish Date:2025/03/15 Views:142 Category:React

When we use the useState hook in our code but forget to import it, it generates the error Uncaught ReferenceError: useState is not defined. To fix this error, you need to import the hook before using it import {useState} from react . // ?️

React error Uncaught ReferenceError: process is not defined solution

Publish Date:2025/03/15 Views:117 Category:React

To resolve the “Uncaught ReferenceError: process is not defined” error in React, open a terminal in the root directory of your project and update the version of the `react-scripts` package by running `npm install react-scripts@latest` and reinstal

Scan to Read All Tech Tutorials

Social Media
  • https://www.github.com/onmpw
  • qq:1244347461

Recommended

Tags

Scan the Code
Easier Access Tutorial