譯註:
原文地址 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年,React
0.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 }; }
這樣就留給咱們另外一個問題: 爲何要傳遞props
?code
你也許以爲對於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.props
和this.context
引用。
經過使用Hooks,就不須要super
和this
了。但那是另外一個主題了。