【譯】爲什麼咱們要寫super(props)?

我據說如今Hooks是新的熱點。諷刺地是,我想描述類的相關事實做爲這片博客的開始。那是怎麼樣的呢?html

這些坑對於有效地使用React並不重要。但若是你想更深刻地瞭解事物的工做原理,你可能會發現它們頗有趣。react

這是第一個。git


個人生命中我寫的 super(props)

在個人生命中我寫的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

爲什麼咱們要調用 super?調用不當會發生什麼呢?


在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!');
    }
}
複製代碼

想象一下thissuper以前被調用是被容許的。一個月後,咱們可能改變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?


你也許認爲傳遞propssuper是必要的,這樣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傳遞propsReact也會當即設置它們。這是有緣由的。

當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。可是那是下次的話題。

原文連接:overreacted.io/why-do-we-w… by Dan Abramov

相關文章
相關標籤/搜索