在React中如何优雅的处理事件响应详解
前言
本文主要给大家介绍的是关于react处理事件响应的相关内容,分享出来供大家参考学习,下面来一起看看详细的介绍吧。
react中定义一个组件,可以通过react.createclass
或者es6的class。本文讨论的react组件是基于class定义的组件。采用class的方式,代码结构更加清晰,可读性强,而且react官方也推荐使用这种方式定义组件。
处理事件响应是web应用中非常重要的一部分。react中,处理事件响应的方式有多种。
一、使用箭头函数
先上代码:
//代码1 class mycomponent extends react.component { render() { return ( <button onclick={()=>{console.log('button clicked');}}> click </button> ); } }
当事件响应逻辑比较复杂时,再把所有的逻辑直接写在onclick的大括号内,就会导致render函数变得臃肿,不容易直观地看出组件render出的元素结构。这时,可以把逻辑封装成组件的一个方法,然后在箭头函数中调用这个方法。
如下所示:
//代码2 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> ); } }
这种方式最大的问题是,每次render调用时,都会重新创建一个事件的回调函数,带来额外的性能开销,当组件的层级越低时,这种开销就越大,因为任何一个上层组件的变化都可能会触发这个组件的render方法。当然,在大多数情况下,这点性能损失是可以不必在意的。这种方式也有一个好处,就是不需要考虑this的指向问题,因为这种写法保证箭头函数中的this指向的总是当前组件。
二、使用组件方法
代码先:
//代码3 class mycomponent extends react.component { constructor(props) { super(props); this.state = {number: 0}; this.handleclick = this.handleclick.bind(this); } handleclick() { this.setstate({ number: ++this.state.number }); } render() { return ( <div> <div>{this.state.number}</div> <button onclick={this.handleclick}> click </button> </div> ); } }
这种方式的好处是每次render,不会重新创建一个回调函数,没有额外的性能损失。需要注意的是,使用这种方式要在构造函数中为事件回调函数绑定this: this.handleclick = this.handleclick.bind(this)
,否则handleclick中的this是undefined。这是因为es6 语法的缘故,es6 的 class 构造出来的对象上的方法默认不绑定到 this 上,需要我们手动绑定。每次都手动绑定this是不是有点蛋疼?好吧,让我们来看下一种方式。
三、属性初始化语法(property initializer syntax)
使用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的插件获取这个特性支持。
四、回调函数传参问题
事件的回调函数默认是会被传入一个事件对象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> ); } }
使用第二种方式的话,可以把绑定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。推荐使用第二种和第三种方式。
好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对的支持。