ISSUE / 005
[译]Why Do We Write super(props)?
Why Do We Write super(props)? 为什么我们要写 super(props) ? 原文:https://overreacted.io/why-do-we-write-super-props/ 备注:自个儿翻译的,翻译如有错误,欢迎指出 听闻最近Hooks相当火 但是今天要分享的还…

Why Do We Write super(props)? 为什么我们要写 super(props) ?
原文:https://overreacted.io/why-do-we-write-super-props/
备注:自个儿翻译的,翻译如有错误,欢迎指出~
听闻最近Hooks相当火~ 但是今天要分享的还是有关于类组件的东西,因吹斯听!
以下内容并不会让你写react更六,但是如果想对这其中的原理感兴趣的话,这会非常有趣。
这是首先要说的事
我这一生写过的super(props)比我吃的米还要多:
class Checkbox extends React.Component {
constructor(props) {
super(props);
this.state = { isOn: true };
}
// ...
}
当然,class fields proposal特性可以让我们的代码更简洁:
class Checkbox extends React.Component {
state = { isOn: true };
// ...
}
2015年的时候,那时 React 0.13 版本增加了对class的支持,我们计划了这种写法。定义一个constructor和调用super(props)一直是一个临时的解决方案直到class field提供了一个更贴合语义的方案。
但是让我们再回过头看看这段代码(no class field):
class Checkbox extends React.Component {
constructor(props) {
super(props);
this.state = { isOn: true };
}
// ...
}
为什么我们要调用super? 我们可以不调用它吗?如果必须要调用,那不传props会发生什么?还可以传其他的参数吗? 欢迎收看本期走进科学~
在JavaScript中,super指代的的是父类的构造函数。(在我们的例子中,super指的是React.Component)
有一点很重要,JavaScript不允许你在super调用之前使用this:
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 folks!');
}
}
想象一下,如果在调用super之前允许使用this。一个月以后,我们可能改变了greetColleagues的实现,如下:
greetColleagues() {
alert('Good morning folks!');
alert('My name is ' + this.name + ', nice to meet you!');
}
但是我们忘记了一点,那就是this.greetColleagues()在super调用前(设置this.name)被调用。这就意味着this.name甚至可能还未定义!正如我们所看到的,这样的代码看起来让人容易产生困惑。
为了避免这样的错误,JavaScript禁止在调用super前使用this。让父类的构造函数做它该做的事!这个限制同样适用于定义为类组件的类:
constructor(props) {
super(props);
// ✅ Okay to use `this` now
this.state = { isOn: true };
}
这给我们留下了另一个问题:为什么要传递props?
你可能认为传递props给super是因为为了让React.Component的构造函数能使用传递的props进行初始化:
// Inside React
class Component {
constructor(props) {
this.props = props;
// ...
}
}
这与真相只有一步之遥 -- 事实上,这才是真相。
但是你可以试一下,即使你调用super()没传递props参数,你仍然能够在render和其他方法中拿到this.props!amazing~
这是怎么回事?事实上,React还会在你调用完构造函数之后再一次将props挂载到实例的属性上:
// Inside React
const instance = new YourComponent(props);
instance.props = props;
因此,即使你忘记了传递props给super,React仍然会对此做修正,这是有原因的。
当React支持类组件时,不仅仅只考虑支持ES6的class。目标是尽可能地支持已有的class实现,尚且不清楚最后ClojureScript,CoffeeScript,ES6, Fable, Scala.js, TypeScript或者其他的定义类组件的方案会有多成功。所以React是有意设计不需要调用super()的。尽管ES6的class需要。
那这是否意味着你可以只写super()而不是super(props)呢?
答案可能是不可以! 当然,React确实会在构造函数之后再次赋值this.props。但是this.props在super()和构造函数结束之间仍然是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
}
// ...
}
如果你在构造函数中调用了类的其他方法,这就有可能给调试带来更多的麻烦。所以我还是推荐总是传递props来调用super(props),尽管这不是必须的。
class Button extends React.Component {
constructor(props) {
super(props); // ✅ We passed props
console.log(props); // ✅ {}
console.log(this.props); // ✅ {}
}
// ...
}
这确保了一点,this.props在super之后构造函数结束之前是有值的。
还有最后一点可能是React忠实粉丝可能会好奇的。
你可能会注意到当你使用Context API在类组件中(不管是旧的contextTypes还是在React 16.6 新添加的ContexTypeAPI),context都作为构造函数的第二个参数被传递。
那为什么我们不写super(props, context)代替呢?当然我们可以这样写,但是因为context使用的不是很频繁,所以这个隐患并没有出现太多。
随着class field提案的提出,这个隐患基本可以说消除了。
没有显式的声明构造函数,所有的参数都将自动传递。这允许像state = {}的表达式直接引用this.props和this.context。
有了Hooks,我们甚至不再需要super和this了,但这又是另外的一个话题了。
