• 如何正确地在React中处理事件


    1、构造器内绑定this

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

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

    2、属性初始化

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

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

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

    3、箭头函数

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

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

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

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

    4、函数传递参数

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

    使用第一种方式(构造器内绑定this)的话,可以把绑定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函数来绑定其他参数。

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

    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>
            );
        }
    }

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

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

  • 相关阅读:
    SOLR企业搜索平台 一 (搭建SOLR)
    org.apache.catalina.util.DefaultAnnotationProcessor cannot be cast to org.ap解决方案
    SOLR企业搜索平台 二 (分词安装)
    SOLR企业搜索平台 三 (schema.xml配置和solrj的使用)
    java读取某个文件夹下的所有文件
    Asp.net中奇怪的Calendar日期
    [持续交付实践] 交付流水线设计:代码静态检查
    [持续交付实践] pipeline使用:语法详解
    [持续交付实践] pipeline使用:快速入门
    [持续交付实践] 开篇:持续集成&持续交付综述
  • 原文地址:https://www.cnblogs.com/crazycode2/p/9075236.html
Copyright © 2020-2023  润新知