【譯】爲何要寫super(props)

譯註:
原文地址 https://overreacted.io/why-do-we-write-super-props/javascript

正文

我據說Hooks是新的熱點。可笑的是,我想經過描述一些關於class組件的事實來做爲第一篇博客。這個想法怎麼樣!
這些陷阱對於有效的使用React並不重要。可是若是你喜歡深刻挖掘運行機制,就會發現這些東西的又去之處。
下面介紹第一個。
我寫過不少次 super(props) 但不少狀況下,我並不瞭解爲何要寫它。html

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

固然,class fields 提案讓咱們能夠跳過這步操做。java

class Checkbox extends React.Component {
  state = { isOn: true };
  // ...
}

以前計劃的一種支持plain class的語法(注:即類變量),已經在2015年,React0.13版中加入。在class fields完整肯定以前,定義 constructor 和調用 super(props) 被當作一種臨時方案。
可是,讓咱們回到只使用ES2015的例子裏:react

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

爲何要調用 super? 能不能不用它?若是不得不用它,在調用它時,發生了什麼?還有其餘的參數嗎?git


在JavaScript裏,super指向父類構造器。(在咱們的例子裏, 他指向React.Component實現類)。
重點在於,若是調用了父類構造器,在調用super以前,沒法使用this關鍵字。JavaScript不容許這麼幹。github

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

爲何JavaScript強制在調用this以前執行父類構造器?這裏有一個好的解釋。考慮一個類的層級結構:ide

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來動態加載信息中的姓名。this

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

可是咱們忘記了this.greetColleagues()是在super()以前調用,它已經和this.name創建了聯繫。而this.name甚至尚未定義。你能發現,像這樣的代碼,真的很難理解。
爲了不這樣的陷阱, 若是在構造器中調用this,JavaScript強制要求super要在this以前書寫,讓父類作它們應該作的事。這條限制也被應用到了React組件類定義中:debug

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

這樣就留給咱們另外一個問題: 爲何要傳遞propscode


你也許以爲對於React.Component構造器初始化this.props而言,經過super傳遞props很是重要。

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

這和真相相去甚遠。事實上,這纔是它作了什麼
但不知道爲啥,即便你不傳入props,直接調用super(),仍是能夠在render和其餘方法裏訪問到this.props(若是你不相信我,本身試一下)。
這是怎麼個狀況?它實際上證明了React也會在調用constructor以後,馬上合併props

// Inside React
const instance = new YourComponent(props);
instance.props = props;

因此即便你忘記把props傳遞給super(),React也會及時設置上去的。下面是緣由之一:

當React支持類方法聲明組件時,並非單單支持了ES6類語法。它的目標是支持全部抽象類範圍內的聲明方法。JavaScript有不少變種,如ClojureScript, CoffeeScript, ES6, Fable, Scala.js, TypeScript,或者是其餘方式,並非很比如較到底哪一種方式去定義一個組件更合適。因此React故意執拗得要求super(),雖然ES6 class是這樣。
這下明白爲何能只寫super()而不用寫super(props)了嗎?
也許還不明白,不要緊,這個東西太使人困惑了。固然,React將會在構造器執行完畢後去合併this.props。可是在super和構造器結尾之間,this.props還是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 
  }
  // ...
}

若是一些方法在構造器中調用,這樣會給debug形成很大的挑戰。這也是爲何我推薦傳遞super(props),雖然它不是必須的。

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

這樣確保了this.props在構造器存在前就已經被設置。


還有一點,React長期使用者可能會好奇。
你也許注意到了Context API傳遞了第二個參數給構造器。(不管是古老的contextTypes仍是如今16.6新加的ContextAPI)。
爲何要寫super(props, context)來代替super(props)?固然也行,可是context不多使用,因此這個陷阱不常出現。
在class fields提案經過以後,這些陷阱都沒得差很少了。沒有一個明確的constructor,全部的參數都會自動傳遞。這也是爲何一個表達式相似state={}能夠包含this.propsthis.context引用。
經過使用Hooks,就不須要superthis了。但那是另外一個主題了。

相關文章
相關標籤/搜索