這篇文章源自 Dan 的博客。javascript
如今的熱點是 hooks,因此 Dan 決定寫一篇關於 class
組件的文章 😂。html
文章中描述的問題,應該不會影響你寫代碼;不過若是你想深刻研究 React 是怎麼工做的,這篇文章可能會對你有幫助。java
第一個問題:react
我本身都不知道我寫了多少遍 super(props)
:git
class Checkbox extends React.Component {
constructor(props) {
super(props);
this.state = { isOn: true };
}
// ...
}
複製代碼
固然,class 屬性提案 能夠簡化代碼:github
class Checkbox extends React.Component {
state = { isOn: true };
// ...
}
複製代碼
2015 年初的時候,React 0.13 版本就已經計劃支持該語法。在此以前,咱們須要不斷地寫 constructor
,而後再調用 super(props)
。ide
如今咱們先回顧一下以前的寫法:函數
class Checkbox extends React.Component {
constructor(props) {
super(props);
this.state = { isOn: true };
}
// ...
}
複製代碼
咱們爲何要調用 super
?能不能不調用?若是調用的時候不傳入props
呢?還能夠傳入其餘參數麼?帶着這些問題往下看。ui
在 JavaScript 中,super
引用的是父類構造函數。(在 React 中,引用的天然就是 React.Component
)this
須要注意的是,在調用父類構造函數以前,沒法用 this
。其實這不是 React 的限制,而是 JavaScript 的限制:
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!');
}
}
複製代碼
若是 JavaScript 容許在調用 super
以前使用 this
,一個月以後,咱們須要修改 greetColleagues
方法,方法中使用了 name 屬性:
greetColleagues() {
alert('Good morning folks!');
alert('My name is ' + this.name + ', nice to meet you!');
}
複製代碼
不過咱們可能已經忘了 this.greetColleagues();
是在調用 super
以前調用的;所以,this.name
尚未賦值。這樣的代碼,很難定位 bug。
爲了不這樣的錯誤,JavaScript 強制開發者在構造函數中先調用 super
,才能使用this
。這一限制,也被應用到了 React 組件:
constructor(props) {
super(props);
// ✅ 如今能夠用 `this` 啦
this.state = { isOn: true };
}
複製代碼
問題又來了:爲何要傳入 props
呢?
你可能覺得必須給 super
傳入 props
,不然 React.Component
就無法初始化 this.props
:
// Inside React
class Component {
constructor(props) {
this.props = props;
// ...
}
}
複製代碼
en...離真相不遠 —— 事實上,React 也的確這麼幹了。
不過,若是你不當心漏傳了 props
,直接調用了 super()
,你仍然能夠在 render
和其餘方法中訪問 this.props
(不信的話能夠試試嘛)。
爲啥這樣也行?由於React 會在構造函數被調用以後,會把 props
賦值給剛剛建立的實例對象:
// Inside React
const instance = new YourComponent(props);
instance.props = props;
複製代碼
props
不傳也能用,是有緣由的。
React 添加對 class 支持的時候,不只僅要支持 ES6 的 class,還須要考慮其餘的 class 實現, CoffeeScript, ES6, Fable, Scala.js, TypeScript 中 class 的使用方式並不一致。因此,即便有了 ES6 class,在調用 super()
這個問題上,React 沒作太多限制。
但這意味着你在使用 React 時,能夠用 super()
代替 super(props)
了麼?
別這麼幹,由於會帶來其餘問題。 雖然 React 會在構造函數運行以後,爲 this.props
賦值,但在 super()
調用以後與構造函數結束以前, this.props
仍然是無法用的。
// Inside React
class Component {
constructor(props) {
this.props = props;
// ...
}
}
// Inside your code
class Button extends React.Component {
constructor(props) {
super(); // 😬 忘了傳入 props
console.log(props); // ✅ {}
console.log(this.props); // 😬 undefined
}
// ...
}
複製代碼
要是構造函數中調用了某個訪問 props
的方法,那這個 bug 就更難定位了。所以我強烈建議始終使用super(props)
,即便這不是必須的:
class Button extends React.Component {
constructor(props) {
super(props); // ✅ We passed props
console.log(props); // ✅ {}
console.log(this.props); // ✅ {}
}
// ...
}
複製代碼
上面的代碼確保 this.props
始終是有值的。
還有一個問題可能困擾 React 開發者好久了。你應該已經注意到,當你在 class 中使用 Context API 時(不管是以前的 contextTypes
仍是如今的 contextType
API),context
都是做爲構造函數的第二個參數。
咱們爲何不用寫 super(props, context)
?咱們固然能夠這麼寫,不過 context API 用的相對較少,因此引起的 bug 也比較少。
感謝class 屬性提案 ,這樣的 bug 幾近絕跡。只要沒有顯式聲明構造函數,全部參數都會被自動傳遞。因此,在state = {}
表達式中,你能夠訪問this.props
以及 this.context
。