講道理,React中,咱們爲何須要寫 super(props)?

這篇文章源自 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.Componentthis

須要注意的是,在調用父類構造函數以前,沒法用 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

相關文章
相關標籤/搜索