原文地址:Why Do We Write super(props) ? 原文做者:Dan Abramov 譯者:Washington Hua
我據說 Hooks 最近很火。諷刺的是,我想以一些關於 class 組件的有趣故事來開始這個博客。怎樣!(皮一下很開心)javascript
這些小坑並不會影響你高效的使用 React,但若是你願意深刻了解下背後的工做原理,你會發現它們很是有意思。html
這是第一個。java
我這輩子寫過的 super(props)
比我想象的要多得多react
class Checkbox extends React.Component {
constructor(props) {
super(props);
this.state = { isOn: true };
}
// ...
}複製代碼
固然,class fields proposal 容許咱們跳過這個儀式。git
class Checkbox extends React.Component {
state = { isOn: true };
// ...
}複製代碼
這樣的語法是在 2015 年 React 0.13 增長對純 Class 的支持的時候加入 計劃 的. 定義 constructor
和調用 super(props)
一直都只是 class fiels 出現以前的臨時解決方案。github
然而,讓咱們只用 ES2015 的特性來回顧一下這個例子。bash
class Checkbox extends React.Component {
constructor(props) {
super(props);
this.state = { isOn: true };
}
// ...
}複製代碼
咱們爲何要調用super
?能不能不調用它?若是非要調用,若是不傳 props
會怎樣?還有其它參數嗎?讓咱們來看一下。函數
在 JavaScript 中,super
指代父類的構造函數。(在咱們的案例中,它指向 React.Component
這個實現)this
重點在於,在你調用父類構造函數以前,你沒法在構造函數中使用 this
。JavaScript 不會容許你這麼作。spa
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
把 person 的 name 加到消息中。
greetColleagues() {
alert('Good morning folks!');
alert('My name is ' + this.name + ', nice to meet you!');
}複製代碼
但咱們忘了 this.greetColleagues()
是在 super()
有機會設置 this.name
以前被調用的。this.name
甚至還沒被定義!如你所見,像這樣的代碼理解起來會很困難。
爲了不這樣的陷阱,JavaScript 強制規定,若是你想在構造函數中只用this
,就必須先調用 super
。讓父類作它該作的事!這一限制也適用於定義成類的 React 組件。
constructor(props) {
super(props);
// ✅ 如今可使用 `this` 了
this.state = { isOn: true };
}複製代碼
這給咱們留下了另外一個問題:爲何要傳 props
?
你或許以爲把 props
傳進 super
是必要的,這使得基類 React.Component
能夠初始化 this.props
:
// React 內部
class Component {
constructor(props) {
this.props = props;
// ...
}
}複製代碼
很接近了——事實上,它就是這麼作的。
然而,即使在調用 super()
時沒有傳入 props
參數,你依然可以在 render
和其它方法中訪問 this.props
。(你要是不相信我,能夠本身試一試)
這是什麼原理?其實 React 在調用你的構造函數以後,立刻又給實例設置了一遍 props
:
// React 內部
const instance = new YourComponent(props);
instance.props = props;複製代碼
所以,即使你忘了把 props
傳入 super()
,React 依然會在過後設置它們。這是有理由的。
當 React 添加對 Class 的支持時,它並非只添加了對 ES6 的支持,而是但願可以支持儘量普遍的 class 抽象。因爲不是很肯定 ClojureScript、CoffeeScript、ES六、Fable、Scala.js、TypeScript 或其餘解決方案誰更適合用來定義組件,React 對因而否有必要調用 super()
刻意不表態。
那麼這是否意味着你能夠只寫 super()
而不用 super(props)
?
或許並不是如此,由於這依然讓人困擾。誠然,React 會在你的構造函數運行以後設置 this.props
。但在 super
調用一直到構造函數結束以前,this.props
依然是未定義的。
// React 內部
class Component {
constructor(props) {
this.props = props;
// ...
}
}
// 你的代碼
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 的長期用戶或許會好奇的。
你或許已經注意到,當你在 Class 中使用 Context API 時(不管是舊版的語法仍是 React 16.6 中新增的現代化語法),context 是被做爲構造函數的第二個參數傳入的。
那麼咱們爲何不寫 super(props, context)
呢?固然咱們能夠這麼作,但 context 的使用頻率沒那麼高,因此這個陷阱影響還沒那麼大。
伴隨着 class fields proposal 的發佈,這個問題也就不復存在了。即使不顯式調用構造函數,全部參數也會自動傳入。這就容許像 state = {}
這樣的表達式在必要時能夠直接引用 this.props.
或 this.context
。
在 Hooks 中,咱們甚至都沒有 super
或 this
。這個話題咱們擇日再說。