[譯]咱們爲何要寫 super(props)?

  • 原文地址:Why Do We Write super(props) ?
  • 原文做者:Dan Abramov
  • 譯者:Washington Hua
  • 我據說 Hooks 最近很火。諷刺的是,我想以一些關於 class 組件的有趣故事來開始這個博客。怎樣!(皮一下很開心)javascript

    這些小坑並不會影響你高效的使用 React,但若是你願意深刻了解下背後的工做原理,你會發現它們很是有意思。html

    這是第一個。java

    我這輩子寫過的 super(props) 比我想象的要多得多react

    class Checkbox extends React.Component {
      constructor(props) {
        super(props);
        this.state = { isOn: true };
      }
      // ...
    }複製代碼

    固然,class fields proposal 容許咱們跳過這個儀式。git

    class Checkbox extends React.Component {
      state = { isOn: true };
      // ...
    }複製代碼

    這樣的語法是在 2015 年 React 0.13 增長對純 Class 的支持的時候加入 計劃 的. 定義 constructor 和調用 super(props) 一直都只是 class fiels 出現以前的臨時解決方案。github

    然而,讓咱們只用 ES2015 的特性來回顧一下這個例子。bash

    class Checkbox extends React.Component {
      constructor(props) {
        super(props);
        this.state = { isOn: true };
      }
      // ...
    }複製代碼

    咱們爲何要調用super?能不能不調用它?若是非要調用,若是不傳 props 會怎樣?還有其它參數嗎?讓咱們來看一下。函數

    在 JavaScript 中,super 指代父類的構造函數。(在咱們的案例中,它指向 React.Component 這個實現)this

    重點在於,在你調用父類構造函數以前,你沒法在構造函數中使用 this。JavaScript 不會容許你這麼作。spa

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

    想象一下若是在調用 super 前使用 this 是被容許的。一個月以後。咱們或許會改變 greetColleagues 把 person 的 name 加到消息中。

    greetColleagues() {
      alert('Good morning folks!');
      alert('My name is ' + this.name + ', nice to meet you!');
    }複製代碼

    但咱們忘了 this.greetColleagues() 是在 super() 有機會設置 this.name 以前被調用的。this.name 甚至還沒被定義!如你所見,像這樣的代碼理解起來會很困難。

    爲了不這樣的陷阱,JavaScript 強制規定,若是你想在構造函數中只用this,就必須先調用 super。讓父類作它該作的事!這一限制也適用於定義成類的 React 組件。

    constructor(props) {
      super(props);
      // ✅ 如今可使用 `this` 了
      this.state = { isOn: true };
    }複製代碼

    這給咱們留下了另外一個問題:爲何要傳 props

    你或許以爲把 props 傳進 super 是必要的,這使得基類 React.Component 能夠初始化 this.props

    // React 內部
    class Component {
      constructor(props) {
        this.props = props;
        // ...
      }
    }複製代碼

    很接近了——事實上,它就是這麼作的

    然而,即使在調用 super() 時沒有傳入 props 參數,你依然可以在 render 和其它方法中訪問 this.props。(你要是不相信我,能夠本身試一試)

    這是什麼原理?其實 React 在調用你的構造函數以後,立刻又給實例設置了一遍 props

    // React 內部
    const instance = new YourComponent(props);
    instance.props = props;複製代碼

    所以,即使你忘了把 props 傳入 super(),React 依然會在過後設置它們。這是有理由的。

    當 React 添加對 Class 的支持時,它並非只添加了對 ES6 的支持,而是但願可以支持儘量普遍的 class 抽象。因爲不是很肯定 ClojureScript、CoffeeScript、ES六、Fable、Scala.js、TypeScript 或其餘解決方案誰更適合用來定義組件,React 對因而否有必要調用 super() 刻意不表態。

    那麼這是否意味着你能夠只寫 super() 而不用 super(props)

    或許並不是如此,由於這依然讓人困擾。誠然,React 會在你的構造函數運行以後設置 this.props。但在 super 調用一直到構造函數結束以前,this.props 依然是未定義的。

    // React 內部
    class Component {
      constructor(props) {
        this.props = props;
        // ...
      }
    }
    
    // 你的代碼
    class Button extends React.Component {
      constructor(props) {
        super(); // 😬 咱們忘了傳入 props
        console.log(props);      // ✅ {}
        console.log(this.props); // 😬 undefined
      }
      // ...
    }複製代碼

    若是這發生在某些從構造函數中調用的函數,調試起來會更加麻煩。這也是爲何我推薦老是使用 super(props) 的寫法,即使這是非必要的

    class Button extends React.Component {
      constructor(props) {
        super(props); // ✅ 咱們傳了 props
        console.log(props);      // ✅ {}
        console.log(this.props); // ✅ {}
      }
      // ...
    }複製代碼

    這樣的寫法確保了 this.props即使在構造函數返回以前就被設置好了。

    最後還有一點是 React 的長期用戶或許會好奇的。

    你或許已經注意到,當你在 Class 中使用 Context API 時(不管是舊版的語法仍是 React 16.6 中新增的現代化語法),context 是被做爲構造函數的第二個參數傳入的。

    那麼咱們爲何不寫 super(props, context) 呢?固然咱們能夠這麼作,但 context 的使用頻率沒那麼高,因此這個陷阱影響還沒那麼大。

    伴隨着 class fields proposal 的發佈,這個問題也就不復存在了。即使不顯式調用構造函數,全部參數也會自動傳入。這就容許像 state = {} 這樣的表達式在必要時能夠直接引用 this.props.this.context

    在 Hooks 中,咱們甚至都沒有 superthis。這個話題咱們擇日再說。

    相關文章
    相關標籤/搜索