[译]咱们为何要写 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。这个话题咱们择日再说。

    相关文章
    相关标签/搜索