events 处理

处理 React elements events 和处理 DOM elements 很相似,但有一些语法区别:

  • React events 命名使用 camelCase 规则,而不是 lowercase
  • 使用 JSX 传入 function 作为 events handler,而不是 string 字符串

HMTL 中处理 events 示例如下:

<button onclick="activateLasers()">
  Activate Lasers
</button>

React 中示例如下:

<button onClick={activateLasers}>
  Activate Lasers
</button>

注意它们的区别之处一个是 event 名称,一个是 handler 定义方式。

另一个区别是在 React 中不能通过 return false 的方式防止 events 的默认行为,需要明确的调用 preventDefault method 来实现。

例如在一个 html 页面中定义一个 a tag 并取消其默认打开新页面的行为,实现如下:

<a href="#" onclick="console.log('The link was clicked.'); return false">
  Click me
</a>

React 中实现同样功能代码如下:

class Link extends React.Component {
    constructor(props) {
        super(props);
        this.handleClick = this.handleClick.bind(this);
    }
    handleClick(e) {
        e.preventDefault();
        console.log('clicked me');
    }
    render() {
        return (
            <a href='#' onClick={this.handleClick}>click me</a>
        )
    }
}

e 表示 synthetic 综合的 event,当前哪个 event 触发了 e 就表示哪一个。使用 bind 绑定 的 method 在调用时会自动将 e 传入 method。下面会对 bind 是什么作出解释。

React events 同原生的 events 不完全相同,查看所有可用的 events 查看官方介绍:https://reactjs.org/docs/events.html

在 React 中一般情况下不需要通过调用 addEventListener 来给 element 添加 event listener。直接在 element 初始化时为其设置 event listener 即可。

当通过 class 来定义 component 时 event handler 一般是一个 class method,例如上面示例的 handleClick

下面的示例我们构建一个 Toggle component 可以让用户通过一个 button 来切换 ON/OFF 状态:

const React = require('react')
const ReactDOM = require('react-dom')

class Toggle extends React.Component {
    constructor(props) {
        super(props);
        this.state = { isToggleOn: true };
        this.handleClick = this.handleClick.bind(this);
    }
    handleClick() {
        this.setState({ isToggleOn: !this.state.isToggleOn });
    }
    render() {
        return (<button onClick={this.handleClick}> { this.state.isToggleOn ? 'ON' : 'OFF'} </button>);
    }
}

ReactDOM.render(<
    Toggle />,
    document.getElementById('root')
);

JSX callback 中使用 this 需要特别注意,JavaScript class 的 methods 默认是相互隔离的,如果没有主动 bind 捆绑 method 到 this,在另一个 method 中使用 this.method 会报错 undefined

如果调用 method 时不写括号() 例如:onClick={this.handleClick} 则需要提前 bind 这个 method 到 this 中,如上面的示例,bind 语法如下:

this.handleClick = this.handleClick.bind(this);

一般将其放在 constructor 中,这样初始化中就会自动执行,当然也可以在调用时直接定义:

<button onClick={this.handleClick.bind(this)}> { this.state.isToggleOn ? 'ON' : 'OFF'} </button>

如果不想使用 bind 语法来处理,那么还有两种方式来处理 class 中 methods 互相隔离这个问题。

第一种叫做 class fields syntax 语法,通过使用 arrow function 的模式定义 method,这样就可以通过通过 this.method 的方法调用 method:

    constructor(props) {
        super(props);
        this.state = { isToggleOn: true };
    }
    handleClick = () => {
        this.setState({ isToggleOn: !this.state.isToggleOn });
    }
    render() {
        return (<button onClick={this.handleClick} > { this.state.isToggleOn ? 'ON' : 'OFF'} </button>);
    }

这样就不需要在 constructor 中定义 bind 同时可以在 callback 中直接调用 this.handleClick。

但是需要注意目前这只是 React 实验性的语法,不一定保证以后会一直可用。

第二种是在 callback 中通过 arrow function 的模式调用 method:

    handleClick() {
        this.setState({ isToggleOn: !this.state.isToggleOn });
    }
    render() {
        return (<button onClick={() => this.handleClick()} > { this.state.isToggleOn ? 'ON' : 'OFF'} </button>);
    }

注意这种方法需要在 method 名称后加括号()

这种方法的缺点是当每次重新 render 渲染时都会创建新的 callback。当这个 callback 包含传给其 child component 的 props 时,可能会导致 child 重新被渲染。通常情况下推荐使用 constructor 定义 bind 或者使用 class fields syntax 语法来避免这些性能问题。

给 event handler 传入数据

有时候需要给 event handler 传入附加的参数,如下面示例 button 点击时输出一个输入数据到终端:

    handleClick(a, e) {
        this.setState({ isToggleOn: !this.state.isToggleOn });
        console.log(e._reactName);
        console.log(a);
    }
    render() {
        return (<button onClick={this.handleClick.bind(this, 'aaa')} > { this.state.isToggleOn ? 'ON' : 'OFF'} </button>);
    }

上面的示例将字符串 aaa 作为 handleClick 的传入参数,并将 bind 绑定过程直接放在 callback 中,这样就不需要在 constructor 中进行 bind 定义了。

通过 bind 绑定后会自动将 e:synthetic 综合的 event 作为第二个参数传入 function,e._reactName 返回 event 名称。

上面的示例也可以通过 arrow function 在 callback 中定义实现:

    handleClick(a, e) {
        this.setState({ isToggleOn: !this.state.isToggleOn });
        console.log(e._reactName);
        console.log(a);
    }
    render() {
        return (<button onClick={(e) => this.handleClick('aaa', e)} > { this.state.isToggleOn ? 'ON' : 'OFF'} </button>);
    }

上面的示例中 e 依然表示 synthetic event。两种方法都会将 e 作为第二个参数传入。在 arrow function 中我们可以清晰地看到数据的位置,但是通过 bind 的方式会将有些参数自动转发过去。

标签: none

添加新评论