前言
在说起react的函数绑定之前,我们有必要了解下bind函数的作用是什么,函数的执行的上下文以及其默认的this是指向的哪里?有点抽象?那么你可以去看下下面的两个方法的执行会有什么区别吧。
let customer={
name:'Robin',
getName:function(){
console.log(this.name);
}
}
customer.getName();//Robin
let getName = customer.getName;
getName();// CodePen
let getNameWithCustomer = customer.getName.bind(customer);
getNameWithCustomer();// Robin
如果你猜不出结果,可以看下我的codePen链接:链接,这是为什么呢?这是因为上下文不同,也就是this不同,当你通过赋值拿到方法,方法执行时会先去查看其上下文的this是什么,而不是直接使用其来源于哪个对象的。那么如果我们想使用对象里的属性,可以使用bind方法绑定一个指定的对象。于是我们又得到了我们期望的name:robin值。
从上面的结果中,我们得到了以下的几个认知:
1 函数调用时,只取决于其实际执行环境,与来源无关
2 如果我们希望函数执行或者函数拷贝时,不丢失参数,或者避免参数错误,我们应该将参数传入,而不要过度依赖当前this的上下文。
3 如果我们只是一个工具函数,那么你可能没有特别的场景一定要绑定什么,比如你定义一个add方法计算两个参数的和,那么你不需要绑定其他的对象或者上下文。
4 如果我们希望函数执行时,在某个环境或者类内绑定指定的上下文,那么你需要显性的绑定this才可以使用这个对象里的属性以及属性方法。
5 如果我们喜欢函数执行时,希望可以根据需求绑定任意的对象,也建议函数定义时,考虑好这一点,并绑定时绑定其对应的对象,这时候很可能你绑定的不是"this"。
6 bind方法只是改变上下文,并不会导致函数的执行。
备注:更多关于this的文章,详解请看我另外两篇文章,有非常详尽的说明:神奇的this:链接, js中的作用域:链接
react中的事件绑定
react中的事件绑定,我们按照无参和有参两种。无参非常简单,直接绑定函数即可。当我们需要传递参数时,刚入门前端的人可能会直接绑定参数。这样会导致两个错误,1 丢失了事件参数 2 导致了函数的直接执行,而不是点击后执行。(作为常识,我们还要知道,默认的点击事件等事件是会返回event对象的哦)。
备注:如果你不清楚前端的事件流机制,可以查看我的js中事件流机制:链接
handleClick(e){
const {type} = e;// click
}
render(){
const text = 'button text';
return (
<button onClick={this.handleClick}></button>
)
}
// 直接传参会导致错误
render(){
const text = 'button text';
return (
<button onClick={this.handleClick(text)}></button>
)
}
// 通过箭头函数 避免这种问题
render(){
const text = 'button text';
return (
<button onClick={(e,text) => this.handleClick(e,text)}></button>
)
}
所以你通过箭头函数避免了上面的错误,但这种使用与我们的常规使用是不同的。为什么呢?因为我们一般在react组件中需要经常使用当前组件的上下文,包括当前组件的state、方法以及传入的属性等。这时候,我们想起了前言中的bind语法,其可以改变上下文的this,支持事件获知的同时,绑定当前对象。而且我们通过bind的语法清楚,其不但可以绑定上下文,还可以支持灵活的传参,如果你不清楚其基本语法,可以查看js- MDN的介绍:链接.
下面的内容借鉴于react中文官网的教程--传递函数给组件,做了简单的整理:原文链接:链接,有以下的几种方式。
// 第一种 :render 中bind绑定
render(){
const text = 'button text';
return (
<button onClick={ this.handleClick.bind(this,text)}></button>
)
}
// 第二种 :constructor bind绑定
constructor(props){
this.handleClick = this.handleClick.bind(this);
}
render(){
const text = 'button text';
return (
<button onClick={ (e,text)=> this.handleClick(e,text)}></button>
)
}
// 第三种 :class func 箭头函数 <=> 上面的调用方法均是常规方法写法
handleClick=()=>{
}
render(){
const text = 'button text';
return (
<button onClick={(e,text)=> this.handleClick(e,text)}></button>
)
}
需要注意的是,在官网文档中,我们看到在rende函数中使用箭头函数以及使用bind绑定都会影响性能,而在constructor中将所有函数绑定一遍又过于繁琐,我们一般的框架中习惯于使用class 属性函数,--箭头函数的方式实现,也就是上面代码中的第三种,同样,我们在react ant的ui框架的input组件中找到了icon的元素事件传递的方式也是第三种,查看链接。参考下面的原代码:
handleReset = (e: React.MouseEvent<HTMLElement, MouseEvent>) => {
this.setValue('', e, () => {
this.focus();
});
};
renderClearIcon(prefixCls: string) {
const { allowClear } = this.props;
const { value } = this.state;
if (!allowClear || value === undefined || value === null || value === '') {
return null;
}
return (
<Icon
type="close-circle"
theme="filled"
onClick={this.handleReset}
className={`${prefixCls}-clear-icon`}
role="button"
/>
);
}
小结
通过本文希望你能清楚我们为什么要bind this,以及如何正确绑定的一些可行方式,和每种方式的优缺点,还有一些如何避免低级错误,导致函数直接执行的原因。同样,我们也知道了一般的ui框架中采用的是哪种方式--类属性的方式。
更多精彩内容跳转:原文语雀连接
寄语
我们一起从从小白到大师。 --- Robin