前言
在說起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