为何咱们要添加 super(props) ?

我据说 Hooks 成了新的焦点。可是呢,我想经过这篇博客来介绍下class声明组件有趣的地方。意下如何?html

下面内容没法提升你的React使用技巧。可是,当你深刻探究事物是如何运行时,将会发现它们所带来的喜悦之情。react

首先来看看第一个例子。git


我写super(props)的次数比我想象中多得多:github

class Checkbox extends React.Component {
  constructor(props) {
    super(props);
    this.state = { isOn: true };
  }
  // ...
}
复制代码

固然,class fields proposal(JS提案)使咱们跳过这个过程:ide

class Checkbox extends React.Component {
  state = { isOn: true };
  // ...
}
复制代码

这是在2015年,在React0.13版本时添加支持的类语法planned。在class fields这个更合理的替代方案出来以前,声明constructor和调用super(props)一直被作为一种临时的解决方案。ui

但在此以前,让咱们回到只使用ES2015的例子:this

class Checkbox extends React.Component {
  constructor(props) {
    super(props);
    this.state = { isOn: true };
  }
  // ...
}
复制代码

为何要调用super?咱们能够调用它吗?若是咱们必须调用它,那调用时不传props会怎么样呢?会有更多的参数吗?来一块儿找找答案。spa


JavaScript中,super是父类constructor的引用。(咱们例子中,它指向React.Component翻译

很重要的一点,你是没法在父类的constructor调用以前在constructor中使用this的,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 会强制父类constructor在你碰 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 folks!');
  }
}
复制代码

假设在super以前容许调用this。一个月以后,咱们可能在greetColleagues的消息中加入了name属性:

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

然而咱们忘记了声明this.namesuper方法被调用以前,已经调用了this.greetColleagues()。以致于this.name变成了undefined的状态!如你所见,代码会所以变得难以揣测。

为了不这种陷阱,JavaScript 强制要求, 若是想在constructor里使用this,就必须先调用super。让父类作好它的事先!这个限制也适用于定义别的React组件:

constructor(props) {
    super(props);
    // ✅ Okay to use `this` now
    this.state = { isOn: true };
  }
复制代码

还有另一个问题,为何要传props


你可能会认为,之因此super里要传props,是为了在React.Component的constructor里初始化this.props

// Inside React
class Component {
  constructor(props) {
    this.props = props;
    // ...
  }
}
复制代码

实际上也差很少是这个缘由,这是确切缘由

但在一些时候,即便在调用super时不传props参数,你仍然能够在render和其余方法中获取到this.props。(若是你不信,本身试下咯!)

这是如何实现的呢?缘由是,在你的组件实例化后,会赋值props属性给实例对象。

// Inside React
  const instance = new YourComponent(props);
  instance.props = props;
复制代码

因此即便忘记传propssuper,React仍然会在以后设置它们,这是有缘由的。

当React添加对类的支持时,它不只仅增长了对ES6的支持,目标是尽量支持普遍的类抽象化。当时咱们还不清楚如ClojureScript, CoffeeScript, ES6, Fable, Scala.js, TypeScript或其余解决方案怎样算成功地定义组件,因此React也就不关心是否须要调用super()了——即使是ES6。

因此说是能够只用super()来替代super(props)吗?

最好不要。由于这样仍然有问题。没错,React能够在你的constructor运行后给this.props赋值。但this.props在调用super和constructor结束前仍然是undefined

// Inside React
class Component {
  constructor(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 
  }
  // ...
}
复制代码

若是在constructor中有某些方法存在这种状况,它将会变得难以调试。这也是为何我一直建议添加super(props),即便没有须要:

class Button extends React.Component {
  constructor(props) {
    super(props); // ✅ We passed props
    console.log(props);      // ✅ {}
    console.log(this.props); // ✅ {}
  }
  // ...
}
复制代码

这确保了this.props在constructor完成以前就被赋值。


最后还有一点是长期以来React使用者可能会感到好奇的。

你可能会注意到当你在类中使用 Context API(不管是过去的contextTypes或是后来在React 16.6中添加的contextTypeAPI,context都会作为constructor的第二个参数用来传递)。

因此咱们为何不用super(props, context)来替代呢?其实咱们能够,但 context 的使用频率较低,因此遇到的坑没有那么多。

当有了class fields proposal,大部分的坑都会消失。在没有标明constructor的状况下,所有参数会被自动传入。这样就容许像state = {}的表达式,若是有须要this.props或者this.context将一样适用。

Hooks中,咱们甚至不须要super或者this。但这要改天再说。

翻译原文Why Do We Write super(props)?

相关文章
相关标签/搜索