React躬行记(6)——事件
React在原生事件的基础上,重新设计了一套跨浏览器的合成事件(SyntheticEvent),在事件传播、注册方式、事件对象等多个方面都做了特别的处理。
一、注册事件
合成事件采用声明式的注册方式,类似于设置HTML属性,但有两点不同:
(1)事件要采用小驼峰的命名法,而不是全部小写,例如onclick要写成onClick。
(2)在JSX中的事件处理程序是一个函数引用,而不是一段字符串,如下代码所示,其中handle()是一个函数。
<button onclick="handle()">提交</button>
<button onClick={handle}>提交</button>
两个<button>元素都注册了点击事件,其中前者采用了传统的事件注册,后者使用了合成事件,通过对比可发现它们之间有两处明显的不同。
React的事件类型是原生事件类型的一个子集,在上面的示例中,为按钮添加了Mouse类型的onClick事件。除了常规的Focus、Form、Touch等类型之外,React还支持Selection、Wheel、Media等有特殊功能的事件类型。
二、事件传播
React中的事件最终都会转换成原生事件,但不会注册到某个真实的DOM元素,而是绑定到根元素,使用事件委托技术进行事件代理,再由一个总的事件监听器管理所有的事件处理程序。
React中的事件传播只有冒泡这一种形式,而冒泡会先在DOM树中向上传播,一直到根元素,然后再从JSX中触发的React元素开始向它的父元素传播,依次执行绑定的事件处理程序。在下面的示例中,分别为页面中的根元素和<body>元素,以及组件中的<div>元素和<button>元素绑定了点击事件,其中<div>是<button>的父元素。
document.documentElement.addEventListener("click", function(e) {
console.log("html");
});
document.body.addEventListener("click", function(e) {
console.log("body");
});
class Btn extends React.Component {
handle1(e) {
console.log("子元素");
}
handle2(e) {
console.log("父元素");
}
render() {
return (
<div onClick={this.handle2}>
<button onClick={this.handle1}>提交</button>
</div>
);
}
}
当点击提交按钮时,在控制台输出的顺序是“body”、“html”、“子元素”、“父元素”。在React的合成事件中,也会包含一个事件对象(例如handle1()函数中的e参数)。如果要停止冒泡传播,那么可以在相应的事件处理程序中调用e.stopPropagation()。
三、事件对象
React基于W3C规范对事件对象进行了一层封装,不仅保证了浏览器的兼容性,还提供了一个访问原生事件对象的入口(即调用自身的nativeEvent属性)。注意,在合成事件中阻止元素的默认行为,不能用返回false的方式,得调用事件对象的preventDefault()方法。
合成事件是被池化的,在执行完事件处理程序后,其参数(即事件对象)的属性将会被丢弃,例如通过定时器异步调用事件对象的type属性,如下所示。
class Btn extends React.Component {
handle(e) {
console.log(e.type); //"click"
setTimeout(function() {
console.log(e.type); //null
}, 0);
}
render() {
return <button onClick={this.handle}>提交</button>;
}
}
点击提交按钮,在控制台首先会输出“click”,然后再输出null。如果想要保持事件对象的属性,那么可以调用它的persist()方法。继续上一个例子,只要在handle()方法中加一句e.persist(),如下代码所示,就能在定时器中输出“click”。
handle(e) {
console.log(e.type); //"click"
setTimeout(function() {
console.log(e.type); //"click"
}, 0);
e.persist();
}
四、this
事件处理程序中的this不会自动绑定,即默认是没有指向的。在下面的例子中,点击提交按钮,会先触发注册的handle()方法,然后在方法内输出的this的值是undefined。
class Btn extends React.Component {
handle() {
console.log(this); //undefined
}
render() {
return <button onClick={this.handle}>提交</button>;
}
}
有两种方式可以解决this指向的问题,分别是箭头函数和bind()方法。
1)箭头函数
将事件处理程序改成箭头函数的形式,就能纠正this的指向,如下代码所示。
class Btn extends React.Component {
handle() {
console.log(this); //Btn实例
}
render() {
return <button onClick={() => this.handle()}>提交</button>;
}
}
箭头函数是不包含this的,在其内部使用的this是从上一层的作用域中继承而来的,其指向的是函数声明时所在的对象,即当前组件的实例,注意,像上面的<button>元素那样,直接在其属性中使用箭头函数,会让组件在每次渲染时,创建一个不同的回调函数。
可以换一种写法,避免每次新建函数,如下代码所示,将箭头函数转移到方法声明的时候。
class Btn extends React.Component {
handle = () => {
console.log(this); //Btn实例
}
render() {
return <button onClick={this.handle}>提交</button>;
}
}
2)bind()
为事件处理程序调用bind()方法,同样也能纠正this的指向,如下代码所示。
class Btn extends React.Component {
handle() {
console.log(this); //Btn实例
}
render() {
return <button onClick={this.handle.bind(this)}>提交</button>;
}
}
函数的bind()方法能创建一个绑定指定对象的函数,在上面的render()方法中,传递给bind()方法的this,其指向的正是当前组件的实例。除了在元素的属性中调用bind()方法之外,还可以将其移到组件的构造函数中,如下所示。
class Btn extends React.Component {
constructor(props) {
super(props);
this.handle = this.handle.bind(this);
}
handle() {
console.log(this); //Btn实例
}
render() {
return <button onClick={this.handle}>提交</button>;
}
}
五、事件处理程序的参数
事件处理程序默认会接收一个事件对象,如果要向其传递额外的参数,那么可以通过两种方式实现。
第一种是用箭头函数,如下代码所示,显式地将所有参数传递给事件处理程序。
class Btn extends React.Component {
handle(e, name) {
console.log(e, name);
}
render() {
return <button onClick={(e) => this.handle(e, "strick")}>提交</button>;
}
}
第二种是用bind()方法,如下代码所示,事件对象会被隐式的传递过去,并且必须位于事件处理程序参数列表的最后。
class Btn extends React.Component {
handle(name, e) {
console.log(name, e);
}
render() {
return <button onClick={this.handle.bind(this, "strick")}>bind</button>;
}
}
React躬行记(6)——事件的更多相关文章
- React躬行记(5)——React和DOM
React实现了一套与浏览器无关的DOM系统,包括元素渲染.节点查询.事件处理等机制. 一.ReactDOM 自React v0.14开始,官方将与DOM相关的操作从React中剥离,组成单独的rea ...
- React躬行记(7)——表单
表单元素是一类拥有内部状态的元素,这些状态由其自身维护,通过这类元素可让用户与Web应用进行交互.HTML中的表单元素(例如<input>.<select>和<radio ...
- React躬行记(8)——样式
由于React推崇组件模式,因此会要求HTML.CSS和JavaScript混合在一起,虽然这与过去的关注点分离正好相反,但是更有利于组件之间的隔离.React已将HTML用JSX封装,而对CSS只进 ...
- React躬行记(9)——组件通信
根据组件之间的嵌套关系(即层级关系)可分为4种通信方式:父子.兄弟.跨级和无级. 一.父子通信 在React中,数据是自顶向下单向流动的,而父组件通过props向子组件传递需要的信息是组件之间最常见的 ...
- React躬行记(3)——组件
组件(Component)由若干个React元素组成,包含属性.状态和生命周期等部分,满足独立.可复用.高内聚和低耦合等设计原则,每个React应用程序都是由一个个的组件搭建而成,即组成React应用 ...
- React躬行记(10)——高阶组件
高阶组件(High Order Component,简称HOC)不是一个真的组件,而是一个没有副作用的纯函数,以组件作为参数,返回一个功能增强的新组件,在很多第三方库(例如Redux.Relay等)中 ...
- React躬行记(11)——Redux基础
Redux是一个可预测的状态容器,不但融合了函数式编程思想,还严格遵循了单向数据流的理念.Redux继承了Flux的架构思想,并在此基础上进行了精简.优化和扩展,力求用最少的API完成最主要的功能,它 ...
- React躬行记(14)——测试框架
测试不仅可以发现和预防问题,还能降低风险.减少企业损失.在React中,涌现了多种测试框架,本节会对其中的Jest和Enzyme做详细的讲解. 一.Jest Jest是由Facebook开源的一个测试 ...
- React躬行记(15)——React Hooks
Hook(钩子)是React v16.8新引入的特性,能以钩子的形式为函数组件附加类组件的状态.生命周期等特性.React的类组件有难以拆分.测试,状态逻辑分散,难以复用等问题,虽然可以通过渲染属性( ...
随机推荐
- MySQL—FTS实现原理介绍PPT
这个PPT是有一天我要给同事讲解MySQL的FTS的实现原理花了一个小时做的.
- (转)移动端自定义返回上一页的方法:history
在实际的应用中,我们常常需要实现在移动app和浏览器中点击返回.后退.上一页等按钮实现自己的关闭页面.调整到指定页面或执行一些其它操作的需求. 那在代码中怎样监听当点击微信.支付宝.百度糯米.百度钱包 ...
- WPF ListView控件设置奇偶行背景色交替变换以及ListViewItem鼠标悬停动画
原文:WPF ListView控件设置奇偶行背景色交替变换以及ListViewItem鼠标悬停动画 利用WPF的ListView控件实现类似于Winform中DataGrid行背景色交替变换的效果,同 ...
- WPF依赖项属性不需要包装属性也可以工作
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threa ...
- JSTL自定义标签 实现forEach循环支持集合.数组
java代码实现 tld配置 JSP页面代码
- 虚拟机安装的kali无法识别本机物联网卡
http://blog.csdn.net/sb985/article/details/76427624
- 生成wsdl代理c#代码
C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.6.1 Tools\wsdl.exe /l:CS /out:d:\ws ...
- UWP开发-自适应布局
了解css的人知道,对于不同的屏幕尺寸,css使用一种名为媒体查询的东东来适用不同的屏幕尺寸,以提升用户体验.当用户使用PC等大屏幕的设备时,网页将呈现一种布局形式:而当用户使用手机等小屏幕设备时,布 ...
- 使用QPainter的drawPixmap()绘制多幅图片 good
众所周知,使用QLabel的setPixmap()就可以将图片显示出来,做视屏解码后显示也可以如此.但是为何我今天还要费力使用基函数drawPixmap()来做绘图?理由有这么些吧: 1.使用QLab ...
- c# 计算字符串和文件的MD5值的方法
快速使用Romanysoft LAB的技术实现 HTML 开发Mac OS App,并销售到苹果应用商店中. <HTML开发Mac OS App 视频教程> 土豆网同步更新:http: ...