前言
在React中,我们对事件命名采用小驼峰式(camelCase),而不是纯小写。
传统HTML、React中JSX写法:
// 传统html
<button onclick="handleClickBtn()">按钮</button>
// React JSX
<button onClick={handleClickBtn}>按钮</button>
在传统HTML写法里,onclick="handleClickBtn()",引号内部是js代码,当触发事件后执行该脚本;
而在React里使用{ }JSX的语法,onClick={handleClickBtn}这句代码React会帮我们添加了监听器,handleClickBtn是当前作用域下的一个指向方法的变量名,但是由于JS函数工作原理问题,在ES6 class语法定义组件中(函数组件则不需要,但它也不能提供状态功能的支持,本文仅讨论class组件),我们需要为它绑定this,否则handleClickBtn内部this的指向会出现异常,指向该函数的调用者(此处是undefined),完整代码:
class Btn extends React.Component {
constructor(props) {
super(props);
// 为了在回调中使用 `this`,这个绑定是必不可少的
this.handleClickBtn= this.handleClickBtn.bind(this);
}
handleClickBtn() {
// ...通过手动绑定this,this指向正常
}
render() {
return (
<button onClick={this.handleClickBtn}>
按钮
</button>
);
}
}
这样每次绑定都要在constructor构造函数里通过bind()方法绑定this的指向,当你的事件处理程序多了会很麻烦。
解决办法有两个:箭头函数,实验性public class fields语法(ES2022)。
一、箭头函数
箭头函数内this是词法作用域,由上下文确定:
class Btn extends React.Component {
constructor(props) {
super(props);
// 什么都不干,就不用写constructor了,一般对state初始化
}
handleClickBtn() {
// ...this指向由调用它的箭头函数的上下文决定
}
render() {
return (
<button onClick={() => this.handleClickBtn()}>
按钮
</button>
);
}
}
代码onClick={() => this.handleClickBtn()},通过箭头函数调动我们的事件处理方法,避免了this指向异常。
注意:
官方文档有提到这样每次渲染组件都会创建不同的回调函数,少数情况下(该回调函数作为prop传入子组件时),可能会进行额外的重新渲染。
二、实验性public class fields语法
详细内容可参考babeljs文档。
也就是将事件处理函数的实现通过箭头函数赋值:handleClickBtn = () => {};
上面用例改写后:
class Btn extends React.Component {
constructor(props) {
super(props);
// 什么都不干,就不用写constructor了,一般对state初始化
}
handleClickBtn = () => {
// ...this指向正常
}
render() {
return (
<button onClick={this.handleClickBtn}>
按钮
</button>
);
}
}
三、手动传参
值得一提的是,当我们需要手动传参时(非回调函数传参),如果你写成onClick={this.handleClickBtn(1)},在渲染后会立刻执行该方法!!!
1.为避免函数立即执行,需要在绑定事件处使用箭头函数了:
class Btn extends React.Component {
constructor(props) {
super(props);
// 什么都不干,就不用写constructor了,一般对state初始化
}
handleClickBtn(num) {
// ...this指向正常
// 此处函数实现用class fields语法也行,不冲突
}
render() {
return (
<div>
<button onClick={() => this.handleClickBtn(1)}>
按钮1
</button>
<button onClick={() => this.handleClickBtn(2)}>
按钮2
</button>
</div>
);
}
}
此时事件处理函数实现用public class fields语法也行,不冲突。
2.在绑定事件处使用bind()传参
<button onClick={this.handleClickBtn(this, 1)}>
按钮1
</button>
bind()绑定了this但并不会立刻执行函数,这样也可以。