1、构造器内绑定this

  1. class MyComponent extends React.Component {
  2. constructor(props) {
  3. super(props);
  4. this.state = {
  5. count: 0
  6. };
  7. this.handleClick = this.handleClick.bind(this);
  8. }
  9.  
  10. handleClick() {
  11. this.setState({
  12. count: ++this.state.count
  13. });
  14. }
  15.  
  16. render() {
  17. return (
  18. <div>
  19. <div>{this.state.count}</div>
  20. <button onClick={this.handleClick}>Click</button>
  21. </div>
  22. );
  23. }
  24. }

这种方式的好处是每次render,不会重新创建一个回调函数,没有额外的性能损失。需要注意的是,使用这种方式要在构造函数中为事件回调函数绑定this: this.handleClick = this.handleClick.bind(this),否则handleClick中的this是undefined。这是因为ES6 语法的缘故,ES6 的 Class 构造出来的对象上的方法默认不绑定到 this 上,需要我们手动绑定。

2、属性初始化

使用ES7的 property initializers,代码可以这样写:

  1. class MyComponent extends React.Component {
  2. constructor(props) {
  3. super(props);
  4. this.state = {
  5. count: 0
  6. };
  7. }
  8.  
  9. handleClick = () => {
  10. this.setState({
  11. count: ++this.state.count
  12. });
  13. }
  14.  
  15. render() {
  16. return (
  17. <div>
  18. <div>{this.state.count}</div>
  19. <button onClick={this.handleClick}>Click</button>
  20. </div>
  21. );
  22. }
  23. }

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

3、箭头函数

  1. class MyComponent extends React.Component {
  2. render() {
  3. return (
  4. <button onClick={()=>{console.log('button clicked');}}>
  5. Click
  6. </button>
  7. );
  8. }
  9. }

当事件响应逻辑比较复杂时,如果再把所有的逻辑直接写在onClick的大括号内,就会导致render函数变得臃肿,不容易直观地看出组件render出的元素结构。这时,可以把逻辑封装成组件的一个方法,然后在箭头函数中调用这个方法。如下所示:

  1. class MyComponent extends React.Component {
  2. constructor(props) {
  3. super(props);
  4. this.state = {
  5. count: 0
  6. };
  7. }
  8. handleClick() {
  9. this.setState({
  10. count: ++this.state.count
  11. });
  12. }
  13. render() {
  14. return (
  15. <div>
  16. <div>{this.state.number}</div>
  17. <button onClick={()=>{this.handleClick();}}>Click</button>
  18. </div>
  19. );
  20. }
  21. }

这种方式最大的问题是,每次render调用时,都会重新创建一个事件的回调函数,带来额外的性能开销,当组件的层级越低时,这种开销就越大,因为任何一个上层组件的变化都可能会触发这个组件的render方法。当然,在大多数情况下,这点性能损失是可以不必在意的。这种方式也有一个好处,就是不需要考虑this的指向问题,因为这种写法保证箭头函数中的this指向的总是当前组件。

4、函数传递参数

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

使用第一种方式(构造器内绑定this)的话,可以把绑定this的操作延迟到render中,在绑定this的同时,绑定额外的参数:

  1. // 代码6
  2. class MyComponent extends React.Component {
  3. constructor(props) {
  4. super(props);
  5. this.state = {
  6. list: [1,2,3,4],
  7. current: 1
  8. };
  9. }
  10.  
  11. handleClick(item) {
  12. this.setState({
  13. current: item
  14. });
  15. }
  16.  
  17. render() {
  18. return (
  19. <ul>
  20. {this.state.list.map(
  21. (item)=>(
  22. <li className={this.state.current === item ? 'current':''}
  23. onClick={this.handleClick.bind(this, item)}>{item}
  24. </li>
  25. )
  26. )}
  27. </ul>
  28. );
  29. }
  30. }

使用第二种方式(属性初始化),解决方案和第一种基本一致:

  1. // 代码7
  2. class MyComponent extends React.Component {
  3. constructor(props) {
  4. super(props);
  5. this.state = {
  6. list: [1,2,3,4],
  7. current: 1
  8. };
  9. }
  10.  
  11. handleClick = (item) => {
  12. this.setState({
  13. current: item
  14. });
  15. }
  16.  
  17. render() {
  18. return (
  19. <ul>
  20. {this.state.list.map(
  21. (item)=>(
  22. <li className={this.state.current === item ? 'current':''}
  23. onClick={this.handleClick.bind(undefined, item)}>{item}
  24. </li>
  25. )
  26. )}
  27. </ul>
  28. );
  29. }
  30. }

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

使用第三种方式(函数传递参数)的话很简单,直接传就可以了:

  1. class MyComponent extends React.Component {
  2. constructor(props) {
  3. super(props);
  4. this.state = {
  5. list: [1,2,3,4],
  6. current: 1
  7. };
  8. }
  9.  
  10. handleClick(item,event) {
  11. this.setState({
  12. current: item
  13. });
  14. }
  15.  
  16. render() {
  17. return (
  18. <ul>
  19. {this.state.list.map(
  20. (item)=>(
  21. <li className={this.state.current === item ? 'current':''}
  22. onClick={(event) => this.handleClick(item, event)}>{item}
  23. </li>
  24. )
  25. )}
  26. </ul>
  27. );
  28. }
  29. }

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

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

如何正确地在React中处理事件的更多相关文章

  1. 如何优雅地在React中处理事件响应&&React绑定onClick为什么要用箭头函数?

    React绑定onClick为什么要用箭头函数? https://segmentfault.com/q/1010000010918131 如何优雅地在React中处理事件响应 https://segm ...

  2. 在React中使用 react-router-dom 编程式路由导航的正确姿势【含V5.x、V6.x】

    ## react-router-dom 编程式路由导航 (v5) ###### 1.push跳转+携带params参数 ```jsx props.history.push(`/b/child1/${i ...

  3. react中引入图片路劲正确但是页面上不显示或者打包后不能正常显示的问题

    一.react中图片引入方式 以前我们用img引入图片只需要如下即可,在react中这样写会报错: <img src="../assets/zzsc1.png" /> ...

  4. 【原】React中,map出来的元素添加事件无法使用

    在使用react中,经常用到react的map函数,用法和jquery里中的map一样,但是,如果你在每个map出来的元素中添加,你会发觉添加的事件无法关联, 比如,我们很多的评论,我需要在每个评论下 ...

  5. react中这些细节你注意过没有?

    react中的一些细节知识点: 1.组件中get的使用(作为类的getter) ES6知识:class类也有自己的getter和setter,写法如下: Class Component { const ...

  6. React中Props 和 State用法

    React中Props 和 State用法 1.本质 一句话概括,props 是组件对外的接口,state 是组件对内的接口.组件内可以引用其他组件,组件之间的引用形成了一个树状结构(组件树),如果下 ...

  7. 在react中使用intro.js的的一些经验

    react逐渐热了起来,但是新的东西毕竟前辈的经验少一些,前段时间自己在react中用到intro.js时,得到的资料甚少,摸索后便将一些心得记录下来了~ 1 intro.js的引入,这一点请看上一篇 ...

  8. React中props与state

    以下内容均为个人理解. 1.state: 在react中,state可以看成管理页面状态的集合(实则一个对象而已),库里面的成员均为页面渲染变量,整个页面为一个状态机,当state发生变化时,页面会重 ...

  9. 深入理解react中的虚拟DOM、diff算法

    文章结构: React中的虚拟DOM是什么? 虚拟DOM的简单实现(diff算法) 虚拟DOM的内部工作原理 React中的虚拟DOM与Vue中的虚拟DOM比较 React中的虚拟DOM是什么?   ...

随机推荐

  1. MySQL 之Navicat Premium 12安装使用、pymysql模块使用、sql注入问题的产生与解决

    本文内容提要: Navicat Premium 12 的介绍.使用. pymysql模块的使用 sql注入问题的产生与解决 -------------------------------------- ...

  2. LCD驱动分析(二)帧缓冲设备作为平台设备

    参考:S3C2440 LCD驱动(FrameBuffer)实例开发<一>   S3C2440 LCD驱动(FrameBuffer)实例开发<二> 1.平台设备注册 1.1在li ...

  3. LeetCode(111) Minimum Depth of Binary Tree

    题目 Given a binary tree, find its minimum depth. The minimum depth is the number of nodes along the s ...

  4. ARM-Linux基本开发步骤

    拿到一块YC2440(s3c2440)的开发板,经过几天的学习,我对arm-linux系统开发步骤有了一些认识.就以开发这个开发板为例,arm-linux开发工作大概分4个部分 1.       硬件 ...

  5. bs4--官文--修改文档树

    修改文档树 Beautiful Soup的强项是文档树的搜索,但同时也可以方便的修改文档树 修改tag的名称和属性 在 Attributes 的章节中已经介绍过这个功能,但是再看一遍也无妨. 重命名一 ...

  6. 一、harbor部署之centos7的基本配置

    1 最小安装centos7 ...安装省略... centos7最小化安装后没ifconfig命令,用 ip addr 命令查看网络信息. 2 配置网络 1.cd /etc/sysconfig/net ...

  7. IS-IS IGP

    is-is  是igp的一种    属于osi的协议 OSI的三层是网络层      包含两种服务  一种是面向连接服务CONS       另一种是无连接服务CLNS CLNS中包含CLNP     ...

  8. HDU 3341 Lost's revenge

    Lost's revenge Time Limit: 5000ms Memory Limit: 65535KB This problem will be judged on HDU. Original ...

  9. pytorch遇到的问题:RuntimeError: randperm is only implemented for CPU

    由此,我们找到sample.py,第51行如下图修改

  10. TOJ 2446: Mint

    2446: Mint Time Limit(Common/Java):2000MS/20000MS     Memory Limit:65536KByteTotal Submit: 4         ...