当前位置: 首页 > 编程日记 > 正文

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

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/

相关文章:

深入JDK源码,这里总有你不知道的知识点!

Java的基础知识有很多&#xff0c;但是我认为最基础的知识应该要属jdk的基础代码&#xff0c;jdk的基础代码里面&#xff0c;有分了很多基础模块&#xff0c;其中又属jdk包下面的lang包最为基础。 我们下面将总结和分析一下lang包下面最为基础和常用的几个部分。 1:常用的对象类…

JS同时上传表单图片和表单信息并把上传信息存入数据库,带php后端源码

微信小程序开发交流qq群 581478349 承接微信小程序开发。扫码加微信。 利用JQ&#xff0c;jquery.form.js&#xff0c;bootstrap实现上传表单图片和表单信息并把上传的图片地址&#xff0c;input填写的信息存入数据库&#xff0c;上传的图片存入服务器。 效果 前端&#xff…

java 的回调函数

在Java中&#xff0c;通常就是编写另外一个类或类库的人&#xff08;李四&#xff09;规定一个接口&#xff0c;然后你&#xff08;张三&#xff09;来实现这个接口&#xff0c;然后把这个实现类的一个对象作为参数传给别人的程序&#xff0c;别人的程序必要时就会通过那个接口…

无导师学习_如何找到一位导师并加快学习速度:新手指南。

无导师学习by Victor Cassone由Victor Cassone 如何找到一位导师并加快学习速度&#xff1a;新手指南。 (How to find a mentor and accelerate your learning: a beginner’s guide.) One of my biggest regrets while learning to program was that I isolated myself too m…

wamp环境下安装imagick扩展

先上图&#xff0c;如下是安装成功后的phpinfo()界面: 安装步骤&#xff1a; 1、先确定安装版本&#xff0c;比如我的的php &#xff1a; php7.0.12 x86 ts 那么就需要三方版本 要一致&#xff1a;imagick软件本身( 如x86 )、php本身( x86 ts (thread safe) )、php扩展php_ima…

php批量修改文件名

微信小程序开发交流qq群 173683895 承接微信小程序开发。扫码加微信。 正文&#xff1a; 代码 <?php header("Content-type: text/html; charsetutf-8"); //利用PHP目录和文件函数遍历用户给出目录的所有的文件和文件夹&#xff0c;修改文件名称 function f…

领域驱动设计 敏捷_反馈失败:发现敏捷数据驱动的致命弱点的风险

领域驱动设计 敏捷by Phil Seaton菲尔西顿(Phil Seaton) 反馈失败&#xff1a;发现敏捷数据驱动的致命弱点的风险 (Feedback fail: discover the risk of Agile’s data-driven Achilles heel) 您是否有遭受数据驱动欺骗的危险&#xff1f; (Are you in danger of data-driven…

微信小程序 点击卡片切换 动画效果

微信小程序开发交流qq群 173683895 承接微信小程序开发。扫码加微信。 正文&#xff1a; html <view classaa><image animation"{{animationData0}}" classimg0 stylez-index: {{index_0}}; bindtapbindtap_img id0 src"{{clubs[0].image}}"&…

Log4J的配置

Log4J简介日志记录功能是一个项目中重要的组成部分&#xff0c;Log4J是APache下的一个开源日志组件&#xff0c;为java开发者提供了很大的便利。 Loggers,日志信息的优先级日志信息的优先级从高到低有ERROR、WARN、 INFO、DEBUG&#xff0c;分别用来指定这条日志信息的重要程度…

Xcode中的NSLog详解

探究原因基本上这种事情一定可以在Apple文档中找到&#xff0c;看NSLog的文档&#xff0c;第一句话就说&#xff1a;Logs an error message to the Apple System Log facility.&#xff0c;所以首先&#xff0c;NSLog就不是设计作为普通的debug log的&#xff0c;而是error log…

java web程序示例_想要建立一些有趣的东西吗? 这是示例Web应用程序创意的列表。...

java web程序示例Interested in learning JavaScript? Get my ebook at jshandbook.com有兴趣学习JavaScript吗&#xff1f; 在jshandbook.com上获取我的电子书 If you’re reading this post, you are probably looking for an idea. You likely want to build a simple app …

python读取文件

请参考&#xff1a;http://www.cnblogs.com/sysuoyj/archive/2012/03/14/2395789.html转载于:https://www.cnblogs.com/yajing-zh/p/6807971.html

[Git/Github] ubuntu 14.0 下github 配置

转载自&#xff1a;http://www.faceye.net/search/77573.html 一&#xff1a;创建Repositories1:首先在github下创建一个帐号。这个不用多说&#xff0c;然后创建一个Repositories。2:然后在ubuntu下安装git相关的东东&#xff1a; 1sudo apt-get install git-core git-gui git…

php ile_get_contents无法请求https连接的解决方法

微信小程序开发交流qq群 173683895 承接微信小程序开发。扫码加微信。 正文&#xff1a; 1.windows下的PHP&#xff0c;只需要到php.ini中把extensionphp_openssl.dll前面的;删掉&#xff0c;重启服务就可以了。

material-ui_满足Material-UI —您最喜欢的新用户界面库

material-uiby Code Realm通过Code Realm 满足Material-UI —您最喜欢的新用户界面库 (Meet Material-UI — your new favorite user interface library) Update (17/05/2018): Material-UI v1.0.0 is out! Check out this post by Olivier.更新 (17/05/2018)&#xff1a;Mate…

day 2 基本类型和函数

列表&#xff0c;元组&#xff0c;字典的转换。list列表是一组可变的元素集合列表是[]括号组成的&#xff0c;[]括号包含所有元素&#xff0c;列表的创建可以传递字符串&#xff0c;也可以传递多个字符串来创建列表。如"asd", / "a","b" ...tupl…

手机号码输入历史记录匹配

微信小程序开发交流qq群 173683895 承接微信小程序开发。扫码加微信。 正文&#xff1a; 效果图 html <view class"top_phone"><input placeholder"请输入手机号" typenumber bindinputtop_phone_input bindfocustop_phone_focus value{{ph…

Ubuntu安装Flash视频插件

http://www.linuxidc.com/Linux/2014-05/101095.htm转载于:https://www.cnblogs.com/acbingo/p/4501987.html

如何使用Web Audio API听到“ Yanny”和“ Laurel”的声音

by _haochuan通过_haochuan 如何使用Web Audio API听到“ Yanny”和“ Laurel”的声音 (How you can hear both “Yanny” and “Laurel” using the Web Audio API) Recently an audio clip asking listeners whether they hear the word “Yanny” or “Laurel” has been c…

SCARA——OpenGL入门学习一、二

参考博客&#xff1a;http://www.cppblog.com/doing5552/archive/2009/01/08/71532.html 简介 最近开始一个机械手臂的安装调试&#xff0c;平面关节型机器人又称SCARA(Selective Compliance Assembly Robot Arm)型机器人,是一种应用于装配作业的机器人手臂。然后找资料学习一下…

[.NET] 《Effective C#》快速笔记 - C# 中的动态编程

《Effective C#》快速笔记 - C# 中的动态编程 静态类型和动态类型各有所长&#xff0c;静态类型能够让编译器帮你找出更多的错误&#xff0c;因为编译器能够在编译时进行大部分的检查工作。C# 是一种静态类型的语言&#xff0c;不过它加入了动态类型的语言特性&#xff0c;可以…

阿里云https证书apache配置

微信小程序开发交流qq群 173683895 承接微信小程序开发。扫码加微信。 正文&#xff1a; 一&#xff1a;下载证书&#xff1b; 二&#xff1a;安装证书 文件说明&#xff1a; 1. 证书文件1534884149377.pem&#xff0c;包含两段内容&#xff0c;请不要删除任何一段内容。…

vue css 应用变量_如何使用CSS Grid和CSS变量快速为应用创建原型

vue css 应用变量CSS Grid and CSS Variables are both huge wins for front-end developers. The former makes it dead simple to create website layouts, while the latter brings the power of variables to your stylesheets.CSS Grid和CSS变量对于前端开发人员都是巨大的…

linux--memcache的安装和使用(转)

memcache是高性能&#xff0c;分布式的内存对象缓存系统&#xff0c;用于在动态应用中减少数据库负载&#xff0c;提升访问速度。据说官方所说&#xff0c;其用户包括twitter、digg、flickr等&#xff0c;都是些互联网大腕呀。目前用memcache解决互联网上的大用户读取是非常流行…

EF 调试跟踪生成的SQL语句

IQueryable query from x in appEntitieswhere x.id 10select x;var sql ((System.Data.Objects.ObjectQuery)query).ToTraceString(); 或者 EF6 &#xff1a; var sql ((System.Data.Entity.Core.Objects.ObjectQuery)query).ToTraceString(); 转载于:https://www.cnblogs…

微信小程序图片自适应宽高比例显示解决方法

微信小程序开发交流qq群 173683895 承接微信小程序开发。扫码加微信。 正文&#xff1a; 解决方案一&#xff1a;写固定宽度&#xff0c;然后使用 image 组件中 mode 属性的 widthFix &#xff1b; 先看效果图&#xff1a; 实现代码&#xff1a; <image classmy_img mo…

初创公司面试要问什么_聘请初创公司的产品设计师时要问的问题

初创公司面试要问什么by Bohdan Kit通过Bohdan Kit 聘请初创公司的产品设计师时要问的问题 (Questions to ask when hiring a product designer for a startup) 在人群中寻找隐藏宝石的方法指南 (A how-to guide on finding hidden gems in the crowd) Finding the right pers…

Select Top在不同数据库中的使用

1. oracle数据库 SELECT * FROM TABLE1 WHERE ROWNUM<N 2. Infomix数据库 SELECT FIRST N * FROM TABLE1 3. DB2数据库 SELECT * ROW_NUMBER() OVER(ORDER BY COL1 DESC) AS ROWNUM WHERE ROWNUM<N 或者 SELECT COLUMN FROM TABLE FETCH FIRST N ROWS ONLY 4. SQL Server…

bat+sqlcmd 批量执行脚本

Hello,此BAT脚本能够帮助开发者将某目录下全部SQL脚本按文件名称依次在指定数据库中批量执行。不用忍受powershell invoke-sqlcmd 的笨重。在指执行时多一种选择。 bat文件 echo off REM ******** ******** General Batch for Starting SQL ******** ******** REM %1 is the n…

突破微信小程序五层层级限制的解决方案

微信小程序开发交流qq群 173683895 承接微信小程序开发。扫码加微信。 正文&#xff1a; 五层的限制只是针对 navigateTo&#xff0c;redirectTo 不受此限制。 navigateTo &#xff1a;保留当前页面&#xff0c;跳转到应用内的某个页面&#xff0c;使用wx.navigateBack可以…