我據說如今Hooks是新的熱點。諷刺地是,我想描述類的相關事實做爲這片博客的開始。那是怎麼樣的呢?html
這些坑對於有效地使用React並不重要。但若是你想更深刻地瞭解事物的工做原理,你可能會發現它們頗有趣。react
這是第一個。git
在個人生命中我寫的super(props)
比我知道的多:github
class Checkbox extends React.Component {
constructor(props) {
super(props);
this.state = { isOn: true };
}
}
// ...
複製代碼
固然,類相關的提議讓咱們跳過了這個儀式:ide
class Checkbox extends React.Component {
state = { isOn: true };
// ...
}
複製代碼
2015年React 0.13增長了對純類的支持時,就計劃了這樣的語法。定義constructor
函數和調用super(props)
一直是一種臨時解決方案,直到類字段提供了一種符合人體工程學的替代方案。函數
可是讓咱們回到這個例子,只使用ES2015特性:ui
class Checkbox extends React.Component {
construtor(props) {
super(props);
this.state = { isOn: true };
}
// ...
}
複製代碼
爲什麼咱們要調用super
?咱們能夠不調用它嗎?若是咱們不得不調用它,不傳props
參數會發生什麼呢?還存在其它的參數嗎? 讓咱們一探究竟。this
在JavaScript中,super
引用的是父類的constructor
(在咱們的例子中,它指向React.Component
實現)。spa
重要的是,你不能使用this
直到你調用父級的constructor
後。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會強制父構造函數在你訪問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 forks!');
}
}
複製代碼
想象一下this
在super
以前被調用是被容許的。一個月後,咱們可能改變greetColleagues
中包含在person中的信息:
greetColleagues() {
alert('Good morning folks!');
alert('My name is ' + this.name + ', nice to meet you!');
}
複製代碼
可是咱們忘記了super()
在有機會設置this.name
以前調用了this.greetemployees()
,所以this.name
都還沒定義。正如你所看到的同樣,很難思考像這樣的代碼。
爲了不這樣的坑,JavaScript會迫使你在使用this
以前先調用super
. 讓父級作它該作的事情!這個限制也適用於定義爲類的React組件:
construtor(props) {
super(props);
// ✅ Okay to use `this` now
this.state = { isOn: true };
}
複製代碼
這裏還存在另一個問題:爲何要傳遞props
?
你也許認爲傳遞props
給super
是必要的,這樣React.Component
構造函數就能初始化this.props
了:
// Inside React
class Component {
constructor(props) {
this.props = props;
// ...
}
}
複製代碼
這與事實相去不遠——確實,事實就是如此。
可是,即便你調用super
沒帶props
參數,你依然能夠在render
或者其它方法中獲取到props
。(若是你不信我,你能夠本身試試!)
那究竟是怎樣工做的呢?事實代表 React
也會在調用構造函數後當即在實例上初始化props
:
// Inside React
const instance = new YourComponent(props);
instance.props = props;
複製代碼
所以即便你忘記了給super
傳遞props
,React
也會當即設置它們。這是有緣由的。
當React添加了對類的支持時,它不只僅只支持ES6中的類。目標是支持儘量普遍的類抽象。目前還不清楚使用ClojureScript
,CoffeeScript
,ES6
,Fable
,Scala.js
,TypeScript
,或者其它方式定義組件會有多成功。所以,React故意不明確是否須要調用super()
——即便ES6的類須要。
因此這就意味着你可使用super()
代替super(props)
了嗎?
可能不是由於它仍然使人困惑。 固然,React會在你運行了constructor
以後爲this.props
賦值。可是在super
調用和構造函數結束之間,this.props
仍然是未定義的:
// Inside React
class Component {
construtor(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
}
// ...
}
複製代碼
若是在從構造函數調用的某個方法中發生這種狀況,調試可能會更加困難。這就是爲何我老是建議你們傳super(props)
,儘管它不是必需的:
class Button extends React.Component {
constructor(props) {
super(props); // ✅ We passed props
console.log(props); // ✅ {}
console.log(this.props); // ✅ {}
}
}
複製代碼
保證在constructor
存在以前this.props
就被設置了。
還有最後一點React用戶可能會感到好奇的。
你可能注意到在類中使用Context API
時(包括舊的contextTypes
和在React16.6
中添加的現代contextType API
),context
做爲第二個參數被傳給constructor
。
爲何咱們不用super(props, context)
代替呢?咱們可以,只是上下文的使用頻率較低,因此這個坑不會常常出現。
隨着類字段的提議,這個坑基本上消失了。 若是沒有顯式構造函數,全部參數都將被自動傳遞下去。這就是容許像state ={}
這樣的表達式包含對this.props
或者this.context
的引用的緣由。
使用Hooks
,咱們甚至不會使用到super
或者this
。可是那是下次的話題。