原文: Why Do We Write super(props)?
本文原文: 為什么寫render(props)
我聽說hooks最近很熱。具有諷刺意味的是,我想通過描述有關(guān)類組件的有趣事實(shí)來開始這個(gè)博客。那會(huì)怎么樣!
這些陷阱對(duì)于有效地使用React并不重要。但是如果你想更深入地了解事情的運(yùn)作方式,你可能會(huì)發(fā)現(xiàn)它們很有趣。
這是第一個(gè)。
我寫過的super(props)比我知道的要多:
class Checkbox extends React.Component {
constructor(props) {
super(props);
this.state = { isOn: true };
}
// ...
}
當(dāng)然,類字段提案讓我們跳過儀式:
class Checkbox extends React.Component {
state = { isOn: true };
// ...
}
當(dāng)React 0.13在2015年增加對(duì)普通類的支持時(shí),計(jì)劃使用這樣的語(yǔ)法。定義構(gòu)造函數(shù)和調(diào)用super(props)成為臨時(shí)解決方案,直到類字段提供符合人體工程學(xué)的替代方案。
但是,讓我們回到這個(gè)例子,只使用ES2015功能:
class Checkbox extends React.Component {
constructor(props) {
super(props);
this.state = { isOn: true };
}
// ...
}
我們?yōu)槭裁凑{(diào)用super?我們可以不調(diào)用它嗎?如果我們必須調(diào)用它,如果我們不傳遞props會(huì)發(fā)生什么?還有其他論點(diǎn)嗎?讓我們來看看。
在JavaScript中,super指的是父類構(gòu)造函數(shù)。(在我們的示例中,它指向React.Component實(shí)現(xiàn)。)
重要的是,在調(diào)用父構(gòu)造函數(shù)之前,不能在構(gòu)造函數(shù)中使用this。 JavaScript不會(huì)讓你:
class Checkbox extends React.Component {
constructor(props) {
// ?? Can’t use `this` yet
super(props);
// ? Now it’s okay though
this.state = { isOn: true };
}
// ...
}
有一個(gè)很好的理由說明為什么JavaScript在你觸摸它之前強(qiáng)制執(zhí)行父構(gòu)造函數(shù)??紤]一個(gè)類層次結(jié)構(gòu):
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!');
}
}
想象一下,在允許調(diào)用super之前使用this。一個(gè)月后,我們可能會(huì)更改greetColleagues以在郵件中包含此人的姓名:
greetColleagues() {
alert('Good morning folks!');
alert('My name is ' + this.name + ', nice to meet you!');
}
但是我們忘記了在super調(diào)用有機(jī)會(huì)設(shè)置this.name之前調(diào)用this.greetColleagues。所以this.name甚至還沒有定義!如您所見,這樣的代碼很難想到。
為了避免這些陷阱,JavaScript強(qiáng)制要求如果你想在構(gòu)造函數(shù)中使用它,你必須首先調(diào)用super。讓父級(jí)做自己的事!此限制也適用于定義為類的React組件:
constructor(props) {
super(props);
// ? Okay to use `this` now
this.state = { isOn: true };
}
這給我們留下了另一個(gè)問題:為什么要傳遞props?
你可能認(rèn)為將props傳遞給super是必要的,以便基本的React.Component構(gòu)造函數(shù)可以初始化this.props:
// Inside React
class Component {
constructor(props) {
this.props = props;
// ...
}
}
這與事實(shí)并不遙遠(yuǎn) - 事實(shí)上,這就是它的作用。
但不知何故,即使你沒有使用props參數(shù)調(diào)用super,你仍然可以在render和其他方法中訪問this.props。 (如果你不相信我,請(qǐng)親自試試?。?/p>
這是如何運(yùn)作的?事實(shí)證明,在調(diào)用構(gòu)造函數(shù)后,React也會(huì)在實(shí)例上分配props:
// Inside React
const instance = new YourComponent(props);
instance.props = props;
因此,即使你忘記將props傳遞給super,React仍會(huì)在之后設(shè)置它們。這是有原因的。
當(dāng)React添加對(duì)類的支持時(shí),它不僅僅增加了對(duì)ES6類的支持。目標(biāo)是盡可能支持廣泛的類抽象。目前尚不清楚ClojureScript,CoffeeScript,ES6,Fable,Scala.js,TypeScript或其他解決方案在定義組件方面的成功程度。所以React故意不關(guān)心是否需要調(diào)用super - 即使是ES6類。
那么這是否意味著你可以只寫super()而不是super(props)?
盡可能不要這樣,否則會(huì)使人困惑。
當(dāng)然,React稍后會(huì)在你的構(gòu)造函數(shù)運(yùn)行后分配this.props。但是this.props在調(diào)用super和構(gòu)造函數(shù)結(jié)束之間仍然是未定義的:
// 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
}
// ...
}
如果在從構(gòu)造函數(shù)調(diào)用的某個(gè)方法中發(fā)生這種情況,則調(diào)試可能更具挑戰(zhàn)性。 這就是為什么我建議總是傳遞super(props),即使它不是絕對(duì)必要的:
class Button extends React.Component {
constructor(props) {
super(props); // ? We passed props
console.log(props); // ? {}
console.log(this.props); // ? {}
}
// ...
}
這樣可以確保在構(gòu)造函數(shù)退出之前設(shè)置this.props。
最后一點(diǎn)React用戶可能會(huì)對(duì)此感到好奇。
您可能已經(jīng)注意到,當(dāng)您在類中使用Context API時(shí)(使用遺留的contextTypes或React 16.6中添加的現(xiàn)代contextType API),上下文將作為第二個(gè)參數(shù)傳遞給構(gòu)造函數(shù)。
那么為什么我們不寫super(props, context)呢?我們可以,但上下文的使用頻率較低,所以這個(gè)陷阱并沒有那么多。
隨著類字段的提議,這整個(gè)陷阱大多數(shù)都會(huì)消失。如果沒有顯式構(gòu)造函數(shù),則會(huì)自動(dòng)傳遞所有參數(shù)。這允許像state = {}這樣的表達(dá)式包含對(duì)this.props或this.context的引用(如果需要)。
有了Hooks,我們甚至沒有super或this。但那是另一天的話題。