by Saurabh Misra

索拉·米斯拉(Saurabh Misra)

这就是为什么我们需要在React的类组件中绑定事件处理程序 (This is why we need to bind event handlers in Class Components in React)

While working on React, you must have come across controlled components and event handlers. We need to bind these methods to the component instance using .bind() in our custom component’s constructor.

在使用React时,您必须遇到受控组件和事件处理程序。 我们需要在自定义组件的构造函数中使用.bind()将这些方法绑定到组件实例。

class Foo extends React.Component{constructor( props ){super( props );this.handleClick = this.handleClick.bind(this);}handleClick(event){// your event handling logic}render(){return (<button type="button" onClick={this.handleClick}>Click Me</button>);}
}ReactDOM.render(<Foo />,document.getElementById("app")
);

In this article, we are going to find out why we need to do this.

在本文中,我们将找出为什么需要这样做。

I would recommend reading about .bind() here if you do not already know what it does.

如果您尚不知道.bind()我建议在这里阅读。

责备JavaScript,无法React (Blame JavaScript, Not React)

Well, laying blame sounds a bit harsh. This is not something we need to do because of the way React works or because of JSX. This is because of the way the this binding works in JavaScript.

好吧,怪罪听起来有些刺耳。 由于React的工作方式或JSX,我们不需要这样做。 这是因为this绑定在JavaScript中的工作方式。

Let’s see what happens if we do not bind the event handler method with its component instance:

让我们看看如果不将事件处理程序方法与其组件实例绑定在一起会发生什么:

class Foo extends React.Component{constructor( props ){super( props );}handleClick(event){console.log(this); // 'this' is undefined}render(){return (<button type="button" onClick={this.handleClick}>Click Me</button>);}
}ReactDOM.render(<Foo />,document.getElementById("app")
);

If you run this code, click on the “Click Me” button and check your console. You will see undefined printed to the console as the value of this from inside the event handler method. The handleClick() method seems to have lost its context (component instance) or this value.

如果运行此代码,请单击“ Click Me”按钮并检查您的控制台。 你会看到undefined打印到控制台的值this从事件处理方法内部。 handleClick()方法似乎丢失了其上下文(组件实例)或this值。

“ this”绑定在JavaScript中的工作方式 (How ‘this’ binding works in JavaScript)

As I mentioned, this happens because of the way this binding works in JavaScript. I won’t go into a lot of detail in this post, but here is a great resource to understand how the this binding works in JavaScript.

正如我提到的,发生这种情况的原因是this绑定在JavaScript中的工作方式。 我不会在本文中详细介绍,但是这里是了解this绑定如何在JavaScript中工作的好资源。

But relevant to our discussion here, the value of this inside a function depends upon how that function is invoked.

但是与我们这里的讨论有关, this功能在函数内部的值取决于该函数的调用方式。

默认绑定 (Default Binding)

function display(){console.log(this); // 'this' will point to the global object
}display();

This is a plain function call. The value of this inside the display() method in this case is the window — or the global — object in non-strict mode. In strict mode, the this value is undefined.

这是一个简单的函数调用。 在这种情况下,在display()方法内部的this的值是非严格模式下的window对象或global对象。 在严格模式下, this值是undefined

隐式绑定 (Implicit binding)

var obj = {name: 'Saurabh',display: function(){console.log(this.name); // 'this' points to obj}
};obj.display(); // Saurabh

When we call a function in this manner — preceded by a context object — the this value inside display() is set to obj.

当我们以这种方式(在上下文对象之前)调用函数时, display()this值设置为obj

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

但是,当我们给这个函数引用其他一些变量,并调用使用这个新功能的参考作用,我们得到的不同的值this里面display()

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

In the above example, when we call outerDisplay(), we don’t specify a context object. It is a plain function call without an owner object. In this case, the value of this inside display() falls back to default binding. It points to the global object or undefined if the function being invoked uses strict mode.

在上面的示例中,当我们调用outerDisplay() ,我们没有指定上下文对象。 这是一个简单的函数调用,没有所有者对象。 在这种情况下, this display()内部的值将退回到默认binding 如果所调用的函数使用严格模式,则它指向全局对象或undefined

This is especially applicable while passing such functions as callbacks to another custom function, a third-party library function, or a built-in JavaScript function like setTimeout .

在将诸如回调之类的函数传递给另一个自定义函数,第三方库函数或内置JavaScript函数(如setTimeout ,这尤其适用。

Consider the setTimeout dummy definition as shown below, and then invoke it.

考虑如下所示的setTimeout虚拟定义,然后调用它。

// A dummy implementation of setTimeout
function setTimeout(callback, delay){//wait for 'delay' millisecondscallback();}setTimeout( obj.display, 1000 );

We can figure out that when we call setTimeout, JavaScript internally assigns obj.display to its argument callback .

我们可以发现,当我们调用setTimeout ,JavaScript在内部将obj.display分配给其参数callback

callback = obj.display;

This assignment operation, as we have seen before, causes the display() function to lose its context. When this callback is eventually invoked inside setTimeout, the this value inside display() falls back to default binding.

如我们之前所见,此分配操作导致display()函数失去其上下文。 当最终在setTimeout内部调用此回调时, display()this值将退回到默认binding

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

显式硬绑定 (Explicit Hard Binding)

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

为避免这种情况,我们可以使用bind()方法将this明确绑定到函数。

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

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

现在,当我们调用outerDisplay()价值thisobjdisplay()

Even if we pass obj.display as a callback, the this value inside display() will correctly point to obj .

即使我们将obj.display作为回调传递, display()this值也将正确指向obj

仅使用JavaScript重新创建方案 (Recreating the scenario using only JavaScript)

In the beginning of this article, we saw this in our React component called Foo . If we did not bind the event handler with this , its value inside the event handler was set as undefined.

在本文的开头,我们在名为Foo的React组件中看到了这一点。 如果我们不this绑定事件处理程序,则事件处理程序内部的值将设置为undefined

As I mentioned and explained, this is because of the way this binding works in JavaScript and not related to how React works. So let’s remove the React-specific code and construct a similar pure JavaScript example to simulate this behavior.

正如我提到和解释的那样,这是因为this绑定在JavaScript中起作用的方式,与React的工作方式无关。 因此,让我们删除特定于React的代码,并构建一个类似的纯JavaScript示例来模拟此行为。

class Foo {constructor(name){this.name = name}display(){console.log(this.name);}
}var foo = new Foo('Saurabh');
foo.display(); // Saurabh// The assignment operation below simulates loss of context
// similar to passing the handler as a callback in the actual
// React Component
var display = foo.display;
display(); // TypeError: this is undefined

We are not simulating actual events and handlers, but instead we are using synonymous code. As we observed in the React Component example, the this value was undefined as the context was lost after passing the handler as a callback — synonymous with an assignment operation. This is what we observe here in this non-React JavaScript snippet as well.

我们不是在模拟实际的事件和处理程序,而是在使用同义代码。 正如我们在React Component示例中观察到的, thisundefined因为在将处理程序作为回调传递后上下文丢失了—与赋值操作同义。 这也是我们在此非React JavaScript代码段中观察到的内容。

“Wait a minute! Shouldn’t the this value point to the global object, since we are running this in non-strict mode according to the rules of default binding?” you might ask.

“等一下! this值不应该指向全局对象,因为我们根据默认绑定规则以非严格模式运行此对象?” 你可能会问。

No. This is why:

不,这是为什么:

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

类声明类表达式的主体以严格模式执行,即构造函数,静态方法和原型方法。 Getter和Setter函数在严格模式下执行。

You can read the full article here.

您可以在此处阅读全文。

So, to prevent the error, we need to bind the this value like this:

因此,为防止错误,我们需要像这样绑定this值:

class Foo {constructor(name){this.name = namethis.display = this.display.bind(this);}display(){console.log(this.name);}
}var foo = new Foo('Saurabh');
foo.display(); // Saurabhvar display = foo.display;
display(); // Saurabh

We don’t need to do this in the constructor, and we can do this somewhere else as well. Consider this:

我们不需要在构造函数中执行此操作,也可以在其他地方执行此操作。 考虑一下:

class Foo {constructor(name){this.name = name;}display(){console.log(this.name);}
}var foo = new Foo('Saurabh');
foo.display = foo.display.bind(foo);
foo.display(); // Saurabhvar display = foo.display;
display(); // Saurabh

But the constructor is the most optimal and efficient place to code our event handler bind statements, considering that this is where all the initialization takes place.

但是考虑到这是所有初始化发生的地方,构造函数是编写我们的事件处理程序bind语句的最理想,最有效的地方。

为什么我们不需要为Arrow函数绑定' this' (Why don’t we need to bind ‘this’ for Arrow functions?)

We have two more ways we can define event handlers inside a React component.

我们还有另外两种方法可以在React组件中定义事件处理程序。

  • Public Class Fields 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 function in the callback

    回调中的箭头功能

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 the arrow functions introduced in ES6. When using these alternatives, our event handler is already automatically bound to the component instance, and we do not need to bind it in the constructor.

这两个都使用ES6中引入的箭头功能。 使用这些替代方法时,我们的事件处理程序已经自动绑定到组件实例,并且不需要在构造函数中绑定它。

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

原因是在箭头函数的情况下, this是按词法绑定的。 这意味着它将封闭函数(或全局)范围的上下文用作this值。

In the case of the public class fields syntax example, the arrow function is enclosed inside the Foo class — or constructor function — so the context is the component instance, which is what we want.

对于公共类字段语法示例,箭头函数包含在Foo类(或构造函数)内部,因此上下文是组件实例,这正是我们想要的。

In the case of the arrow function as callback example, the arrow function is enclosed inside the render() method, which is invoked by React in the context of the component instance. This is why the arrow function will also capture this same context, and the this value inside it will properly point to the component instance.

在使用箭头函数作为回调示例的情况下,箭头函数包含在render()方法内,该方法由React在组件实例的上下文中调用。 这就是为什么arrow函数也将捕获相同的上下文,并且其中的this值将正确指向组件实例的原因。

For more details regarding lexical this binding, check out this excellent resource.

有关词法this绑定的更多详细信息,请查看此出色的资源 。

使长话短说 (To make a long story short)

In Class Components in React, when we pass the event handler function reference as a callback like this

在React的Class Components中,当我们将事件处理函数的引用作为回调传递时,就像这样

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

the event handler method loses its implicitly bound context. When the event occurs and the handler is invoked, the this value falls back to default binding and is set to undefined , as class declarations and prototype methods run in strict mode.

事件处理程序方法将失去其隐式绑定的上下文。 当事件发生并调用处理程序时, this值将返回默认绑定 ,并设置为undefined ,因为类声明和原型方法以严格模式运行。

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

当我们将事件处理程序的this绑定到构造函数中的组件实例时,我们可以将其作为回调传递,而不必担心会丢失其上下文。

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

箭头函数不受此行为的影响,因为它们使用词法 this 绑定this 绑定会自动将它们绑定到定义它们的作用域。

翻译自: https://www.freecodecamp.org/news/this-is-why-we-need-to-bind-event-handlers-in-class-components-in-react-f7ea1a6f93eb/

这就是为什么我们需要在React的类组件中绑定事件处理程序相关推荐

  1. react中绑定点击事件_在React中绑定事件处理程序的最佳方法

    react中绑定点击事件 by Charlee Li 通过李李 在React中绑定事件处理程序的最佳方法 (The best way to bind event handlers in React) ...

  2. react取消捕获_React中阻止事件冒泡的问题详析

    前言 最近在研究react.redux等,网上找了很久都没有完整的答案,索性自己整理下,这篇文章就来给大家介绍了关于React阻止事件冒泡的相关内容,下面话不多说了,来一起看看详细的介绍吧 在正式开始 ...

  3. dw上按钮事件 pb_「React TS3 专题」使用 TS 的方式在类组件里定义事件

    在「React TS3 专题」亲自动手创建一个类组件,我们一起学习了如何用 TS 的方式在React 里定义类组件(class component)以及了解了什么是 JSX.本篇文章,笔者将带着大家一 ...

  4. React之类式组件中的构造器与props

    <!DOCTYPE html> <html> <head> <meat charset="UTF-8"> <title> ...

  5. react 不能往组件中传入属性的值为 undefined

    在使用 andt design 的时候遇到个需求,需要清除 Select 组件选中后的值,让它变成什么都没选中,显示 placeholder 刚开始以为设置为 null 即可,结果发现设置为 null ...

  6. 在ES6类中绑定事件

    概述 ES6提供了类,给模块化带来了很大的帮助.在类里面绑定事件,一来是为了使得代码结构清晰,二来是为了可以使用类的变量和方法.但是,由于事件的回调函数并不是由类的实例对象触发,所以,事件回调函数里面 ...

  7. React基础篇(六)React中绑定事件的注意点

    本小节讲述在 React 中为 button 设置点击事件的注意点 1 前言 在 React 中,事件的名称都是 React 中提供的,因此名称的首字母必须厉害 例如 onClick onMouseO ...

  8. react类组件中父组件调用子组件函数

    1.自定义事件 子组件在 componentDidMount 生命周期里调用父组件的方法将 this 传给父组件 Son import React, { Component } from 'react ...

  9. React类组件的两种写法

    react中类组件的两种写法: 要点:•类组件必须要继承React.Component •类组件中的render()方法,返回值是一个jsx // 方式一: import React from 're ...

最新文章

  1. python编程和c语言编程的区别-通过实例浅析Python对比C语言的编程思想差异
  2. 美国人的网站推广方式千奇百怪
  3. 找工作java还是python有用_你觉得学 Python 还是 Java 更好找工作?
  4. 进入Linux救援(rescue)模式的四大法门
  5. Centos7 开启网卡配置IP并连接xshell——转
  6. 去掉知乎/CSDN网页标题未读消息提示
  7. 手把手玩转win8开发系列课程(14)
  8. 因为马云,这也许是他们过得最有意思的腊八节
  9. 最强内存稳定性测试软件,内存稳定性测试软件Memtest正式版——HyperX FURY DDR4成绩喜人...
  10. 达梦数据库角色、用户管理
  11. iOS开发面试和底层学习视频整理合集
  12. 完美解决failed to open stream: HTTP request failed!(file_get_contents引起的)
  13. 简信CRM:超实用的移动CRM选型参考指南
  14. java 三维_java之三维数组
  15. JSRPC: WebSocket报错 Refused to connect to ‘ws:<URL>/ws?group=testname=wangluozhe‘ because it violate
  16. 卡尔曼滤波实现java
  17. 用html制作发帖与回帖,发帖代码
  18. 帆软相同列合并_excel表中同一列相同内容进行合并 又快又好
  19. MPLS Virtual Private Network
  20. 01_家庭用电预测:线性回归算法(时间与功率功率与电流之间的关系)

热门文章

  1. 简单快速修改大量重复代码(Intellij IDEA)
  2. NSURLConnection的简单使用
  3. Vue mixins(混入) 附代码示例详解
  4. 微信网页开发 jssdk前后端代码,PHP实现完整代码,自定义分享
  5. Rushcrm:企业部署CRM系统做什么
  6. 《为iPad而设计:打造畅销App》——大胆创意
  7. 数据传送到后端(二)
  8. c语言中如何设计和编写一个应用系统?
  9. 微软职位内部推荐-Software Engineer II-News
  10. 数据连接linux网络编程之TCP/IP基础(四):TCP连接的建立和断开、滑动窗口