React 事件处理
React 事件是一个合成事件,JSX实际上是 React.createElement(component, props, ...children)
函数提供的语法糖表达方式,那么:
// JSX 表达
<button onClick = {this.handleClick}>
Click me
</button>
// 经转化为
React.createElement('button', {
onClick: this.handleClick
}, 'Click me');
可见,react事件处理可以理解为组件加载(mount
)和更新(update
)时,将通过addEventListener
统一注册到 docuemnt
上,然后有一个事件池存储了所有的事件,当事件触发时,通过 dispatchEvent
进行事件分发。
所以可以简单理解为,this.handleClick
作为回调函数调用。
回调函数this丢失问题,为什么要绑定this?
在函数内部,this的值取决于函数的调用方式,如下:
class Foo {
sayThis () {
console.log(this); // 这里的 `this` 指向谁?
}
exec (cb) {
cb();
}
render () {
this.exec(this.sayThis);
}
}
var foo = new Foo();
foo.render(); // 输出结果是什么?
/*
结果分析:输出为 undefined,
原因:你必须谨慎对待JSX中回调函数的this,在JavaScript中,class方法默认不会绑定
this,如果忘记了绑定 this.handleClick 并传入了 onClick,这时调用这个函数时的
this为undefined。
这并不是react的特有行为,与js工作原理有关,通常情况下,如果你没有在方法后面添
加 (),例如 onClick={this.handleClick},你应该为这个方法绑定 this。
*/
为什么react没有自动把bind集成到render方法中呢?如下:
class Foo {
sayThis () {
console.log(this); // 这里的 `this` 指向谁?
}
exec (cb) {
cb.bind(this)();
}
render () {
this.exec(this.sayThis);
}
}
var foo = new Foo();
foo.render(); // 输出结果是什么?
/*
结果分析:Foo {}
原因:因为render多次调用每次都要bind会影响性能,所以官方建议在constructor中手
动bind达到性能优化。
*/
不同事件处理对比
1. 直接bind this
class Foo extends React.Component {
handleClick () {
this.setState({ xxx: aaa })
}
render() {
return (
<button onClick={this.handleClick.bind(this)}>
Click me
</button>
)
}
}
优点: 写起来顺手
缺点: 性能不太好,这么处理跟react给你内部绑定this没什么区别,每次render都会bind,并且不同地方使用函数都需用bind,这样会多写代码,性能不是最优的
2. constructor中手动bind
class Foo extends React.Component {
constructor(props) {
super(prosp);
this.handleClick = this.handleClick.bind(this);
}
handleClick () {
this.setState({ xxx: aaa })
}
render() {
return (
<button onClick={this.handleClick}>
Click me
</button>
)
}
}
优点 相比第一种,性能较好,因为构造函数只执行一次,那么bind只有一次
缺点 写法上不是很顺手,每次写完方法,都需要专门到constructor中bind
3. 箭头函数型
class Foo extends React.Component {
handleClick () {
this.setState({ xxx: aaa })
}
render() {
return (
<button onClick={(e) => {this.handleClick(e)}}>
Click me
</button>
)
}
}
优点 写法上简洁、顺手
缺点 每次render都会重复创建,性能较差
4. public class fields 型
class LoggingButton extends React.Component {
// 此语法确保 `handleClick` 内的 `this` 已被绑定。
// 注意: 这是 *实验性* 语法。
handleClick = () => {
console.log('this is:', this);
}
render() {
return (
<button onClick={this.handleClick}>
Click me
</button>
);
}
}