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

前端之家收集整理的这篇文章主要介绍了如何优雅地在React中处理事件响应前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

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

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

1.使用箭头函数

先上代码

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

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

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

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

2.使用组件方法

代码先:

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

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

3.属性初始化语法(property initializer Syntax)

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

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

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

4.回调函数传参问题

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

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

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

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

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

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

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

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

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

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

猜你在找的React相关文章