如何优雅地在React中处理事件响应

React中定义一个组件,能够经过React.createClass或者ES6的class。本文讨论的React组件是基于class定义的组件。采用class的方式,代码结构更加清晰,可读性强,并且React官方也推荐使用这种方式定义组件。前端

处理事件响应是Web应用中很是重要的一部分。React中,处理事件响应的方式有多种。react

1.使用箭头函数

先上代码:git

//代码1
class MyComponent extends React.Component {

  render() {
    return (
      <button onClick={()=>{console.log('button clicked');}}>
        Click
      </button>
    );
  }
}

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

//代码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指向的老是当前组件。babel

2.使用组件方法

代码先:app

//代码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是否是有点蛋疼?好吧,让咱们来看下一种方式。函数

3.属性初始化语法(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的transform-class-properties插件获取这个特性支持。this

4.回调函数传参问题

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

使用第一种方式的话很简单,直接传就能够了:

//代码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。推荐使用第二种和第三种方式。


欢迎关注个人公众号:老干部的大前端,领取21本大前端精选书籍!

3808299627-5a93ba468b59a

相关文章
相关标签/搜索