爲何咱們要添加 super(props) ?

我據說 Hooks 成了新的焦點。可是呢,我想經過這篇博客來介紹下class聲明組件有趣的地方。意下如何?html

下面內容沒法提升你的React使用技巧。可是,當你深刻探究事物是如何運行時,將會發現它們所帶來的喜悅之情。react

首先來看看第一個例子。git


我寫super(props)的次數比我想象中多得多:github

class Checkbox extends React.Component {
  constructor(props) {
    super(props);
    this.state = { isOn: true };
  }
  // ...
}
複製代碼

固然,class fields proposal(JS提案)使咱們跳過這個過程:ide

class Checkbox extends React.Component {
  state = { isOn: true };
  // ...
}
複製代碼

這是在2015年,在React0.13版本時添加支持的類語法planned。在class fields這個更合理的替代方案出來以前,聲明constructor和調用super(props)一直被作爲一種臨時的解決方案。ui

但在此以前,讓咱們回到只使用ES2015的例子:this

class Checkbox extends React.Component {
  constructor(props) {
    super(props);
    this.state = { isOn: true };
  }
  // ...
}
複製代碼

爲何要調用super?咱們能夠調用它嗎?若是咱們必須調用它,那調用時不傳props會怎麼樣呢?會有更多的參數嗎?來一塊兒找找答案。spa


JavaScript中,super是父類constructor的引用。(咱們例子中,它指向React.Component翻譯

很重要的一點,你是沒法在父類的constructor調用以前在constructor中使用this的,JavaScript不容許你這樣作:調試

class Checkbox extends React.Component {
  constructor(props) {
    // 🔴 Can’t use `this` yet
    super(props);
    // ✅ Now it’s okay though
    this.state = { isOn: true };
  }
  // ...
}
複製代碼

JavaScript 會強制父類constructor在你碰 this 前被執行是有緣由的。想一想類的層次結構:

class Person {
  constructor(name) {
    this.name = name;
  }
}

class PolitePerson extends Person {
  constructor(name) {
    this.greetColleagues(); // 🔴 This is disallowed, read below why
    super(name);
  }
  greetColleagues() {
    alert('Good morning folks!');
  }
}
複製代碼

假設在super以前容許調用this。一個月以後,咱們可能在greetColleagues的消息中加入了name屬性:

greetColleagues() {
    alert('Good morning folks!');
    alert('My name is ' + this.name + ', nice to meet you!');
  }
複製代碼

然而咱們忘記了聲明this.namesuper方法被調用以前,已經調用了this.greetColleagues()。以致於this.name變成了undefined的狀態!如你所見,代碼會所以變得難以揣測。

爲了不這種陷阱,JavaScript 強制要求, 若是想在constructor裏使用this,就必須先調用super。讓父類作好它的事先!這個限制也適用於定義別的React組件:

constructor(props) {
    super(props);
    // ✅ Okay to use `this` now
    this.state = { isOn: true };
  }
複製代碼

還有另一個問題,爲何要傳props


你可能會認爲,之因此super裏要傳props,是爲了在React.Component的constructor裏初始化this.props

// Inside React
class Component {
  constructor(props) {
    this.props = props;
    // ...
  }
}
複製代碼

實際上也差很少是這個緣由,這是確切緣由

但在一些時候,即便在調用super時不傳props參數,你仍然能夠在render和其餘方法中獲取到this.props。(若是你不信,本身試下咯!)

這是如何實現的呢?緣由是,在你的組件實例化後,會賦值props屬性給實例對象。

// Inside React
  const instance = new YourComponent(props);
  instance.props = props;
複製代碼

因此即便忘記傳propssuper,React仍然會在以後設置它們,這是有緣由的。

當React添加對類的支持時,它不只僅增長了對ES6的支持,目標是儘量支持普遍的類抽象化。當時咱們還不清楚如ClojureScript, CoffeeScript, ES6, Fable, Scala.js, TypeScript或其餘解決方案怎樣算成功地定義組件,因此React也就不關心是否須要調用super()了——即使是ES6。

因此說是能夠只用super()來替代super(props)嗎?

最好不要。由於這樣仍然有問題。沒錯,React能夠在你的constructor運行後給this.props賦值。但this.props在調用super和constructor結束前仍然是undefined

// Inside React
class Component {
  constructor(props) {
    this.props = props;
    // ...
  }
}

// Inside your code
class Button extends React.Component {
  constructor(props) {
    super(); // 😬 We forgot to pass props
    console.log(props);      // ✅ {}
    console.log(this.props); // 😬 undefined 
  }
  // ...
}
複製代碼

若是在constructor中有某些方法存在這種狀況,它將會變得難以調試。這也是爲何我一直建議添加super(props),即便沒有須要:

class Button extends React.Component {
  constructor(props) {
    super(props); // ✅ We passed props
    console.log(props);      // ✅ {}
    console.log(this.props); // ✅ {}
  }
  // ...
}
複製代碼

這確保了this.props在constructor完成以前就被賦值。


最後還有一點是長期以來React使用者可能會感到好奇的。

你可能會注意到當你在類中使用 Context API(不管是過去的contextTypes或是後來在React 16.6中添加的contextTypeAPI,context都會作爲constructor的第二個參數用來傳遞)。

因此咱們爲何不用super(props, context)來替代呢?其實咱們能夠,但 context 的使用頻率較低,因此遇到的坑沒有那麼多。

當有了class fields proposal,大部分的坑都會消失。在沒有標明constructor的狀況下,所有參數會被自動傳入。這樣就容許像state = {}的表達式,若是有須要this.props或者this.context將一樣適用。

Hooks中,咱們甚至不須要super或者this。但這要改天再說。

翻譯原文Why Do We Write super(props)?

相關文章
相關標籤/搜索