我據說 Hooks 成了新的焦點。可是呢,我想經過這篇博客來介紹下class聲明組件有趣的地方。意下如何?html
下面內容沒法提升你的React使用技巧。可是,當你深刻探究事物是如何運行時,將會發現它們所帶來的喜悅之情。react
首先來看看第一個例子。git
我寫super(props)
的次數比我想象中多得多:github
class Checkbox extends React.Component {
constructor(props) {
super(props);
this.state = { isOn: true };
}
// ...
}
複製代碼
固然,class fields proposal(JS提案)使咱們跳過這個過程:ide
class Checkbox extends React.Component {
state = { isOn: true };
// ...
}
複製代碼
這是在2015年,在React0.13版本時添加支持的類語法planned。在class fields這個更合理的替代方案出來以前,聲明constructor
和調用super(props)
一直被作爲一種臨時的解決方案。ui
但在此以前,讓咱們回到只使用ES2015的例子:this
class Checkbox extends React.Component {
constructor(props) {
super(props);
this.state = { isOn: true };
}
// ...
}
複製代碼
爲何要調用super
?咱們能夠不調用它嗎?若是咱們必須調用它,那調用時不傳props
會怎麼樣呢?會有更多的參數嗎?來一塊兒找找答案。spa
JavaScript中,super
是父類constructor的引用。(咱們例子中,它指向React.Component
)翻譯
很重要的一點,你是沒法在父類的constructor調用以前在constructor中使用this
的,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 會強制父類constructor在你碰 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 folks!');
}
}
複製代碼
假設在super
以前容許調用this
。一個月以後,咱們可能在greetColleagues
的消息中加入了name
屬性:
greetColleagues() {
alert('Good morning folks!');
alert('My name is ' + this.name + ', nice to meet you!');
}
複製代碼
然而咱們忘記了聲明this.name
的super
方法被調用以前,已經調用了this.greetColleagues()
。以致於this.name
變成了undefined
的狀態!如你所見,代碼會所以變得難以揣測。
爲了不這種陷阱,JavaScript 強制要求, 若是想在constructor裏使用this
,就必須先調用super
。讓父類作好它的事先!這個限制也適用於定義別的React組件:
constructor(props) {
super(props);
// ✅ Okay to use `this` now
this.state = { isOn: true };
}
複製代碼
還有另一個問題,爲何要傳props
?
你可能會認爲,之因此super
裏要傳props
,是爲了在React.Component
的constructor裏初始化this.props
:
// Inside React
class Component {
constructor(props) {
this.props = props;
// ...
}
}
複製代碼
實際上也差很少是這個緣由,這是確切緣由。
但在一些時候,即便在調用super
時不傳props
參數,你仍然能夠在render
和其餘方法中獲取到this.props
。(若是你不信,本身試下咯!)
這是如何實現的呢?緣由是,在你的組件實例化後,會賦值props
屬性給實例對象。
// Inside React
const instance = new YourComponent(props);
instance.props = props;
複製代碼
因此即便忘記傳props
給super
,React仍然會在以後設置它們,這是有緣由的。
當React添加對類的支持時,它不只僅增長了對ES6的支持,目標是儘量支持普遍的類抽象化。當時咱們還不清楚如ClojureScript, CoffeeScript, ES6, Fable, Scala.js, TypeScript或其餘解決方案怎樣算成功地定義組件,因此React也就不關心是否須要調用super()
了——即使是ES6。
因此說是能夠只用super()
來替代super(props)
嗎?
最好不要。由於這樣仍然有問題。沒錯,React能夠在你的constructor運行後給this.props
賦值。但this.props
在調用super
和constructor結束前仍然是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
}
// ...
}
複製代碼
若是在constructor中有某些方法存在這種狀況,它將會變得難以調試。這也是爲何我一直建議添加super(props)
,即便沒有須要:
class Button extends React.Component {
constructor(props) {
super(props); // ✅ We passed props
console.log(props); // ✅ {}
console.log(this.props); // ✅ {}
}
// ...
}
複製代碼
這確保了this.props
在constructor完成以前就被賦值。
最後還有一點是長期以來React使用者可能會感到好奇的。
你可能會注意到當你在類中使用 Context API(不管是過去的contextTypes
或是後來在React 16.6中添加的contextType
API,context
都會作爲constructor的第二個參數用來傳遞)。
因此咱們爲何不用super(props, context)
來替代呢?其實咱們能夠,但 context 的使用頻率較低,因此遇到的坑沒有那麼多。
當有了class fields proposal,大部分的坑都會消失。在沒有標明constructor的狀況下,所有參數會被自動傳入。這樣就容許像state = {}
的表達式,若是有須要this.props
或者this.context
將一樣適用。
Hooks中,咱們甚至不須要super
或者this
。但這要改天再說。