文:徐超,《React进阶之路》作者

授权发布,转载请注明作者及出处


React 深入系列5:事件处理

React 深入系列,深入讲解了React中的重点概念、特性和模式等,旨在帮助大家加深对React的理解,以及在项目中更加灵活地使用React。

Web应用中,事件处理是重要的一环,事件处理将用户的操作行为转换为相应的逻辑执行或界面更新。在React中,处理事件响应的方式有多种,本文将详细介绍每一种处理方式的用法、使用场景和优缺点。

使用匿名函数

先上代码:

//代码1
class MyComponent extends React.Component {
render() {
return (
<button onClick={()=>{console.log('button clicked');}}>
Click
</button>
);
}
}

点击Button的事件响应函数是一个匿名函数,这应该是最常见的处理事件响应的方式了。这种方式的好处是,简单直接。哪里需要处理事件响应,就在哪里定义一个匿名函数处理。代码1中的匿名函数使用的是箭头函数,我们也可以不使用箭头函数:

//代码2
class MyComponent extends React.Component {
render() {
return (
<button onClick={function(){console.log('button clicked');}}>
Click
</button>
);
}
}

虽然代码2的运行效果和代码1相同,但实际项目中很少见到代码2的这种写法。这是因为箭头函数解决了this绑定的问题,可以将函数体内的this绑定到当前对象,而不是运行时调用函数的对象。如果响应函数中需要使用this.state,那么代码2就无法正常运行了。所以项目中一般直接使用箭头函数定义的匿名函数作为事件响应。

使用匿名函数的缺点是:当事件响应逻辑比较复杂时,匿名函数的代码量会很大,会导致render函数变得臃肿,不容易直观地看出组件最终渲染出的元素结构。另外,每次render方法调用时,都会重新创建一个匿名函数对象,带来额外的性能开销,当组件的层级越低时,这种开销就越大,因为任何一个上层组件的变化都可能会触发这个组件的render方法。当然,在大多数情况下,这点性能损失是可以不必在意的。

使用组件方法

代码如下:

//代码3
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {number: 0};
this.handleClick = this.handleClick.bind(this); // 手动绑定this
} handleClick() {
this.setState({
number: ++this.state.number
});
} render() {
return (
<div>
<div>{this.state.number}</div>
<button onClick={this.handleClick}>
Click
</button>
</div>
);
}
}

点击Button的事件响应函数是组件的方法:handleClick。这种方式的好处是:每次render方法的调用,不会重新创建一个新的事件响应函数,没有额外的性能损失。但是,使用这种方式要在构造函数中为作为事件响应的方法(handleClick),手动绑定this: this.handleClick = this.handleClick.bind(this),这是因为ES6 语法的缘故,ES6 Class 的方法默认不会把this绑定到当前的实例对象上,需要我们手动绑定。每次都手动绑定this是不是有点繁琐?好吧,让我们来看下一种方式。

使用属性初始化语法

使用ES7的属性初始化语法( property initializers ),代码可以这样写:

//代码4
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {number: 0};
} handleClick = () => {
this.setState({
number: ++this.state.number
});
} render() {
return (
<div>
<div>{this.state.number}</div>
<button onClick={this.handleClick}>
Click
</button>
</div>
);
}
}

这样一来,再也不用手动绑定this了。但是你需要知道,这个特性还处于试验阶段,默认是不支持的。如果你是使用官方脚手架Create React App 创建的应用,那么这个特性是默认支持的。你也可以自行在项目中引入babel的transform-class-properties插件获取这个特性支持。

事件响应函数的传参问题

事件响应函数默认是会被传入一个事件对象Event作为参数的。如果想传入其他参数给响应函数应该怎么办呢?

使用第一种方式的话很简单,直接使用新参数:

//代码5
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
list: [1,2,3,4],
current: 1
};
} handleClick(item,event) {
this.setState({
current: item
});
} render() {
return (
<ul>
{this.state.list.map(
(item)=>(
<li className={this.state.current === item ? 'current':''}
onClick={(event) => this.handleClick(item, event)}>{item}
</li>
)
)}
</ul>
);
}
}

onClick的响应函数中,方法体内可以直接使用新的参数item。

使用第二种方式的话,可以把绑定this的操作延迟到render中,在绑定this的同时,绑定额外的参数:

//代码6
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
list: [1,2,3,4],
current: 1
};
} handleClick(item) {
this.setState({
current: item
});
} render() {
return (
<ul>
{this.state.list.map(
(item)=>(
<li className={this.state.current === item ? 'current':''}
onClick={this.handleClick.bind(this, item)}>{item}
</li>
)
)}
</ul>
);
}
}

使用第三种方式,解决方案和第二种基本一致:

//代码7
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
list: [1,2,3,4],
current: 1
};
} handleClick = (item) => {
this.setState({
current: item
});
} render() {
return (
<ul>
{this.state.list.map(
(item)=>(
<li className={this.state.current === item ? 'current':''}
onClick={this.handleClick.bind(undefined, item)}>{item}
</li>
)
)}
</ul>
);
}
}

不过这种方式就有点鸡肋了,因为虽然你不需要通过bind函数绑定this,但仍然要使用bind函数来绑定其他参数。

关于事件响应函数,还有一个地方需要注意。不管你在响应函数中有没有显式的声明事件参数Event,React都会把事件Event作为参数传递给响应函数,且参数Event的位置总是在其他自定义参数的后面。例如,在代码6和代码7中,handleClick的参数中虽然没有声明Event参数,但你依然可以通过arguments[1]获取到事件Event对象。

总结一下,三种事件处理的方式,第一种有额外的性能损失;第二种需要手动绑定this,代码量增多;第三种用到了ES7的特性,目前并非默认支持,需要Babel插件的支持,但是写法最为简洁,也不需要手动绑定this。一般推荐使用第二种和第三种方式。


新书推荐《React进阶之路》

作者:徐超

毕业于浙江大学,硕士,资深前端工程师,长期就职于能源物联网公司远景智能。8年软件开发经验,熟悉大前端技术,拥有丰富的Web前端和移动端开发经验,尤其对React技术栈和移动Hybrid开发技术有深入的理解和实践经验。



美团点评广告平台大前端团队招收2019\2020年前端实习生(偏动效方向)

有意者邮件:yao.zhou@meituan.com

React 深入系列5:事件处理的更多相关文章

  1. React 深入系列4:组件的生命周期

    文:徐超,<React进阶之路>作者 授权发布,转载请注明作者及出处 React 深入系列4:组件的生命周期 React 深入系列,深入讲解了React中的重点概念.特性和模式等,旨在帮助 ...

  2. React学习系列

    React学习系列 系列学习react 翻译地址 https://scotch.io/tutorials/learning-react-getting-started-and-concepts 我是初 ...

  3. React文档翻译系列(三)JSX简介

    # React文档翻译系列(三)JSX简介 先来看一下下面的变量声明: ``` const element = Hello world! ``` 这种有趣的标签语法既不是字符串也不是HTML. 这种形 ...

  4. React文档翻译系列(二)Hello World

    这是React文档翻译系列的第二篇,前一篇介绍了如何安装react,本篇主要介绍react的知识体系,掌握了基本的知识体系,才能更好的学习React. Hello World 开始React最简单的方 ...

  5. React 深入系列1:React 中的元素、组件、实例和节点

    文:徐超,<React进阶之路>作者 授权发布,转载请注明作者及出处 React 深入系列,深入讲解了React中的重点概念.特性和模式等,旨在帮助大家加深对React的理解,以及在项目中 ...

  6. React 深入系列2:组件分类

    文:徐超,<React进阶之路>作者 授权发布,转载请注明作者及出处 React 深入系列2:组件分类 React 深入系列,深入讲解了React中的重点概念.特性和模式等,旨在帮助大家加 ...

  7. React 深入系列3:Props 和 State

    文:徐超,<React进阶之路>作者 授权发布,转载请注明作者及出处 React 深入系列3:Props 和 State React 深入系列,深入讲解了React中的重点概念.特性和模式 ...

  8. 【REACT NATIVE 系列教程之十二】REACT NATIVE(JS/ES)与IOS(OBJECT-C)交互通信

    http://blog.csdn.net/xiaominghimi/article/details/51586492 一用到跨平台的引擎必然要有引擎与各平台原生进行交互通信的需要.那么Himi先讲解R ...

  9. 七天接手react项目 系列 —— react 脚手架创建项目

    其他章节请看: 七天接手react项目 系列 react 脚手架创建项目 前面我们一直通过 script 的方式学习 react 基础知识,而真实项目通常是基于脚手架进行开发. 本篇首先通过 reac ...

随机推荐

  1. Android简易实战教程--第十五话《在外部存储中读写文件》

    第七话里面介绍了在内部存储读写文件 点击打开链接. 这样有一个比较打的问题,假设系统内存不够用,杀本应用无法执行,或者本应用被用户卸载重新安装后.以前保存的用户名和密码都不会得到回显.所以,有必要注意 ...

  2. 在javascript里 string 和 int 类型转换

    string 转换为int 类型 (1)tostring()方法 var   x=10    a   =   x.toString() //输出为string类型 alert(typeof(a)); ...

  3. C++对象模型的那些事儿之六:成员函数调用方式

    前言 C++的成员函数分为静态函数.非静态函数和虚函数三种,在本系列文章中,多处提到static和non-static不影响对象占用的内存,而虚函数需要引入虚指针,所以需要调整对象的内存布局.既然已经 ...

  4. 09 ExpanableListView 的代码例子

    <span style="font-size:18px;">package com.qf.day09_expandablelistview03; import andr ...

  5. 【Unity Shaders】Vertex Magic —— 访问顶点颜色

    本系列主要参考<Unity Shaders and Effects Cookbook>一书(感谢原书作者),同时会加上一点个人理解或拓展. 这里是本书所有的插图.这里是本书所需的代码和资源 ...

  6. 【unix网络编程第三版】阅读笔记(二):套接字编程简介

    unp第二章主要将了TCP和UDP的简介,这些在<TCP/IP详解>和<计算机网络>等书中有很多细致的讲解,可以参考本人的这篇博客[计算机网络 第五版]阅读笔记之五:运输层,这 ...

  7. Mybatis源码之Statement处理器BaseStatementHandler(二)

    BaseStatementHandler是一个抽象类,并没有实现和CURD相关的类,只是更多的设置了一些参数相关. 源码如下: /** * @author Clinton Begin */ publi ...

  8. android打包方法超过65k错误

    近日,Android Developers在Google+上宣布了新的Multidex支持库,为方法总数超过65K的Android应用提供了官方支持. 如果你是一名幸运的Android应用开发者,正在 ...

  9. LOV里的值直接引用系统里定义的值集的值,且具有值集的安全性控制

    fnd_flex_server.check_value_security(p_security_check_mode => 'YH', p_flex_value_set_id => p_f ...

  10. 如何利用BI搭建电商数据分析平台

    某电商是某大型服装集团下的重要销售平台.2015 年,该集团品牌价值达数百亿元,产品质量.市场占有率.出口创汇.销售收入连年居全国绒纺行业第一,在中国有终端店3000多家,零售额80 亿.其羊绒制品年 ...