react之bind函式到元件通識篇

RobinsonZhang發表於2019-04-06

前言

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

相關文章