React 内置组件的事件处理
React 内置组件指的是React中已经定义好的,可以直接使用的如 div、button、input 等与原生 HTML 标签对应的组件
第一种方式:
<a href="#" οnclick="console.info('click me');return false;">click me</a>
这种方式,用来注册事件的 HTML 属性的值是一个字符串,是一段需要执行的 JavaScript 代码
可以通过 return false; 来阻止当前 HTML 元素的默认行为,如 a 标签的页面跳转
第二种方式:
<a href="#" id="my_link">click me</a>
<script>
document.qureySelector('#my_link').addEventListener('click',(e)=>{
e.preventDefault();
console.info('click me');
})
</script>
在React中,事件注册与方式一非常类似,但是有一下不同:
属性名采用驼峰式(如:onClick,onKeyDown),而不是全小写字母
属性值接受一个函数,而不是字符串
return false;不会阻止组件的默认行为,需要调用 e.preventDefault()
示例:
function ActionLink(){
function handleClick(e){
e.preventDefault();
console.log('The link was clicked');
}
return(
<a href="#" onClick={handleClick}>
Click me
</a>
)
}
这是一个以函数方式定义的组件,组件渲染一个a元素,设置链接的点击事件,通过事件处理函数接收到了事件对象(e)
阻止了链接的默认行为,并且打印了 The link was clicked
React 事件对象 VS 原生的 DOM 事件对象
React 中的事件对象可以称之为 SyntheticEvent(合成对象),这样做的最大的好处就是可以屏蔽浏览器的差异
各种厂商的浏览器对规范的实现程度是不一样的,如果直接使用原生DOM事件对象的话,需要考虑浏览器兼容性
而React通过 SyntheticEvent,使在任何支持 React 的浏览器下,事件对象都有一致的接口
React 中所有的事件处理函数都会接收到一个SyntheticEvent 的实例 e 作为参数对象,如果在某些特殊的场景中
需要使用原生的DOM事件对象,可以通过e.nativeEvent 来获取
不要在异步过程中使用 React 事件对象
React 并不是为每一个事件处理函数生成一个全新的事件对象,事件对象会被复用,当事件处理函数被执行以后
事件对象的所有属性会被设置成 null,所以在事件处理函数中,不能以异步的方式使用React的事件对象
因为此时的事件对象的所有属性都是 null,或者已经不是之前的那个事件了
尽量不要使用 addEventListener
React 内部自己实现了一套高效的事件机制,为了提高框架的性能,React通过DOM事件冒泡,只在 document 节点上注册原生的DOM事件
React 内部自己管理所有组件的事件处理函数,以及事件的冒泡、捕获
所以说,如果通过 addEventListener 注册了某个DOM节点的某事件处理函数,并且通过 e.stopPropagation(); 阻断了事件的冒泡或者捕获
那么该节点下所有的节点上,同类型的 React 事件处理函数都会失效
示例:
class CounterLink extends React.Component{
constructor(props){
super(props);
this.state={count:0}
this.handleClick=this.handleClick.bind(this)
}
componentDidMount(){
document.querySelector('.my-link').addEventListener('click',(e)=>{
console.info('raw click');
e.stopPropagation();
})
}
handleClick(e){
e.preventDefault();
console.info('react click');
this.setState({count:this.state.count+1});
}
render(){
return (
<div className="my-link">
<a href="#" onClick={this.handleClick}>Click me {this.state.count} times</a>
</div>
)
}
}
ReactDOM.render(<CounterLink>,document.querySelector("#root"))
示例中虽然设置了链接的点击事件,但是就无法执行
示例截图:
如何绑定事件处理函数的 this
在以类继承的方式定义的组件中,为了能方便的调用当前组件的其他成员方法或属性( 如 : this.state ),
通常需要将事件处理函数运行时的this指向当前组件实例
示例:
class Link extends React.Component{
constructor(props){
super(props);
this.state={count:0}
}
handleClick(e){
e.preventDefault();
this.setState({count:this.state.count+1})
}
render(){
return <a href="#" onClick={this.handleClick}>Click me {this.state.count} times</a>
}
}
ReactDOM.render(<link/>,document.querySelector("#root"))
示例解析:
当点击链接时,控制台会报错 Uncaught TypeError: Cannot read property 'setState' of undefined
是因为没有将 handleClick 运行时的this绑定到当前组件
绑定事件处理函数的 this 到当前组件,有以下几种方式:
第一种方式,通过 bind 方法,原地绑定事件处理函数的 this 指向
示例:
<a href="#" onClick={this.handleClick.bind(this)}>
Click me {this.state.count}
</a>
示例解析:
这种方式的优点是书写起来相对简单,但是每次渲染都会指向 bind 方法生成一个新的函数,会有额外的开销
由于事件处理函数是作为属性传递的,所以从而导致子组件进行重新渲染
第二种方式,通过一个箭头函数将真实的事件处理函数包装一下
示例:
<a href="#" onClick={e=>this.handleClick(e)}>
Clicked me {this .state.count} times
</a>
示例解析:
这种方式书写也不麻烦,但是也没有解决第一种方式面临的性能开销和重新渲染的问题
不过这种方式的一个好处就是能清晰描述事件处理函数接收的参数列表
第三种方式,在constructor中,预先将所有的事件处理函数通过bind方法进行绑定
示例:
class Link extends React.Component{
constructor(props){
super(props);
this.state={count:0}
//重点在这里
this.handleClick=this.handleClick.bind(this)
}
handleClick(e){
e.preventDefault();
this.setState({count:this.state.count+1})
}
render(){
return <a href="#" onClick={this.handleClick}>Click me {this.state.count} times</a>
}
}
ReactDOM.render(<link/>,document.querySelector("#root"))
示例解析:
这种方式能解决前两种方式面临的额外开销和重新渲染的问题,但是写起来略微复杂
一个事件处理函数要分别在三个不同的地方进行定义、绑定this和使用
第四种方式,使用类的成员字段定义语法
示例:
class Link extends React.Component{
constructor(props){
super(props);
this.state={count:0}
}
handleClick=e=>{
e.preventDefault();
this.setState({count:this.state.count+1})
}
render(){
return <a href="#" onClick={this.handleClick}> Click me {this.state.count} times</a>
}
}
ReactDOM.render(<Link/>,document.querySelector("#root"))
示例解析:
这种方式解决了上面三种方式面临的性能开销、重新渲染已经书写麻烦的问题
总结:
React中通过设置组件的事件属性来注册事件,React 内部自己实现了一套包括冒泡、捕获逻辑在内的事件在内的事件机制,所以尽量不要用addEventListener
事件处理函数绑定this的方法有四种,推荐使用类属性定义的方式来定义事件处理函数
事件对象在 React 中是被复用的,事件回调被执行以后,事件对象所有的属性都会被重置为 null,所以不要在异步的过程中使用事件对象
文件借鉴: http://react-china.org/t/react-react/15548