Why do you need to bind event handlers in React Class Components?
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 window
or 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
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.
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.
React tutorial: Types of Props for child components
Publish Date:2025/03/16 Views:170 Category:React
-
Usually, the child components of a React component are a group, that is, the child components are an array. Introduction to Type of the Children Props.
How to solve the error Uncaught TypeError: Cannot read properties of undefined in
Publish Date:2025/03/16 Views:150 Category:React
-
In the process of React development, we often encounter some errors. Here we look at an error reported in App.js. The error is as follows: App.js:69 Uncaught TypeError: Cannot read properties of undefined (reading 'setState') at onInput
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
How to solve the "Function components cannot have string refs" error in React
Publish Date:2025/03/15 Views:139 Category:React
-
When we use a string as a reference in a function component, we get the error "Function components cannot have string refs". To fix this error, we need to use the useRef() hook to get a mutable ref object that we can use as a component.