[譯]爲何要寫 super(props)

原文:overreacted.io/why-do-we-w…javascript

我據說 Hooks 最近很火。諷刺的是,我想用一些關於 class 組件的有趣故事來開始這篇文章。你以爲如何?html

本文中這些坑對於你正常使用 React 並非很重要。 可是假如你想更深刻的瞭解它的運做方式,就會發現實際上它們頗有趣。java

開始第一個。react


首先在個人職業生涯中寫過的 super(props) 本身都記不清:git

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

固然,在類字段提案 (class fields proposal) 中建議讓咱們跳過這個開頭:github

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

在2015年 React 0.13 增長對普通類的支持時,曾經打算用這樣的語法。定義 constructor 和調用 super(props) 始終是一個臨時的解決方案,可能要等到類字段可以提供在工程學上不那麼反人類的替代方案。typescript

不過仍是讓咱們回到前面這個例子,此次只用ES2015的特性:ide

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

爲何咱們要調用super? 能夠調用它嗎? 若是必需要調用,不傳遞prop參數會發生什麼? 還有其餘參數嗎? 接下來咱們試一試:函數


在 JavaScript 中,super 指的是父類的構造函數。(在咱們的示例中,它指向React.Component 的實現。)ui

重要的是,在調用父類構造函數以前,你不能在構造函數中使用this。 JavaScript 是不會讓你這樣作的:

class Checkbox extends React.Component {
  constructor(props) {
    // 🔴 這裏還不能用 `this` 
    super(props);
    // ✅ 如今能夠用了
    this.state = { isOn: true };
  }
  // ...
}
複製代碼

爲何 JavaScript 在使用this以前要先強制執行父構造函數,有一個很好的理由可以解釋。 先看下面這個類的結構:

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

class PolitePerson extends Person {
  constructor(name) {
    this.greetColleagues(); // 🔴 這行代碼是無效的,後面告訴你爲何
    super(name);
  }
  greetColleagues() {
    alert('Good morning folks!');
  }
}
複製代碼

若是容許在調用 super 以前使用this的話。一段時間後,咱們可能會修改 greetColleagues,並在提示消息中添加Personname

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

可是咱們忘記了 super() 在設置 this.name 以前先調用了 this.greetColleagues()。 因此此時 this.name 尚未定義! 如你所見,像這樣的代碼很難想到問題出在哪裏。

爲了不這類陷阱,JavaScript 強制要求:若是想在構造函數中使用this,你必須首先調用super。 先讓父類作完本身的事! 這種限制一樣也適用於被定義爲類的 React 組件:

constructor(props) {
    super(props);
    // ✅ 在這裏能夠用 `this`
    this.state = { isOn: true };
  }
複製代碼

這裏又給咱們留下了另外一個問題:爲何要傳 props 參數?


你可能認爲將props傳給super是必要的,這可使 React.Component 的構造函數能夠初始化this.props

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

這與正確答案很接近了 —— 實際上它就是這麼作的

可是不知道爲何,即使是你調用 super 時沒有傳遞 props 參數,仍然能夠在 render 和其餘方法中訪問this.props。 (不信你能夠親自去試試!)

這是到底是爲何呢? 事實證實,在調用構造函數後,React也會在實例上分配 props

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

所以,即便你忘記將props傳給 super(),React 仍然會在以後設置它們。 這是有緣由的。

當 React 添加對類的支持時,它不只僅增長了對 ES6 類的支持。它的目標是儘量普遍的支持類抽象。 目前還不清楚 ClojureScript、CoffeeScript、ES六、Fable、Scala.js、TypeScript或其餘解決方案是如何相對成功地定義組件的。 因此 React 故意不關心是否須要調用 super() —— 即便是ES6類。

那麼這是否是就意味着你能夠寫 super() 而不是super(props)呢?

可能不行,由於它仍然是使人困惑的。 固然,React 稍後會在你的構造函數運行後分配 this.props, 可是在調用 super() 以後和構造函數結束前這段區間內 this.props 仍然是未定義的:

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

// Inside your code
class Button extends React.Component {
  constructor(props) {
    super(); // 😬 咱們忘記了傳遞 props 參數
    console.log(props);      // ✅ {}
    console.log(this.props); // 😬 undefined 
  }
  // ...
}
複製代碼

若是這種狀況發生在從構造函數調用的某個方法中,可能會給調試工做帶來很大的麻煩。 這就是爲何我建議老是調用 super(props) ,即便在沒有必要的狀況之下:

class Button extends React.Component {
  constructor(props) {
    super(props); // ✅ 傳遞了 props 參數
    console.log(props);      // ✅ {}
    console.log(this.props); // ✅ {}
  }
  // ...
}
複製代碼

這樣就確保了可以在構造函數退出以前設置好 this.props


最後一點是長期以來 React 用戶老是感到好奇的。

你可能已經注意到,當你在類中使用Context API時(不管是舊版的 contextTypes 或在 React 16.6中新添加的 contextType API),context 會做爲第二個參數傳遞給構造函數。

那麼爲何咱們不寫成 super(props, context) 呢? 咱們能夠這樣作,可是使用 context的頻率較低,因此這個坑並無那麼多影響。

根據類字段提案的說明,這些坑大部分都會消失。 若是沒有顯式構造函數,則會自動傳遞全部參數。 這容許在像 state = {} 這樣的表達式中包含對 this.propsthis.context 的引用(若是有必要的話)。

而有了 Hooks 以後,咱們甚至再也不有 superthis 。 不過這是另一個的話題了。

相關文章
相關標籤/搜索