这篇文章源自 Dan 的博客。javascript
如今的热点是 hooks,因此 Dan 决定写一篇关于 class
组件的文章 😂。html
文章中描述的问题,应该不会影响你写代码;不过若是你想深刻研究 React 是怎么工做的,这篇文章可能会对你有帮助。java
第一个问题:react
我本身都不知道我写了多少遍 super(props)
:git
class Checkbox extends React.Component {
constructor(props) {
super(props);
this.state = { isOn: true };
}
// ...
}
复制代码
固然,class 属性提案 能够简化代码:github
class Checkbox extends React.Component {
state = { isOn: true };
// ...
}
复制代码
2015 年初的时候,React 0.13 版本就已经计划支持该语法。在此以前,咱们须要不断地写 constructor
,而后再调用 super(props)
。ide
如今咱们先回顾一下以前的写法:函数
class Checkbox extends React.Component {
constructor(props) {
super(props);
this.state = { isOn: true };
}
// ...
}
复制代码
咱们为何要调用 super
?能不能不调用?若是调用的时候不传入props
呢?还能够传入其余参数么?带着这些问题往下看。ui
在 JavaScript 中,super
引用的是父类构造函数。(在 React 中,引用的天然就是 React.Component
)this
须要注意的是,在调用父类构造函数以前,没法用 this
。其实这不是 React 的限制,而是 JavaScript 的限制:
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!');
}
}
复制代码
若是 JavaScript 容许在调用 super
以前使用 this
,一个月以后,咱们须要修改 greetColleagues
方法,方法中使用了 name 属性:
greetColleagues() {
alert('Good morning folks!');
alert('My name is ' + this.name + ', nice to meet you!');
}
复制代码
不过咱们可能已经忘了 this.greetColleagues();
是在调用 super
以前调用的;所以,this.name
尚未赋值。这样的代码,很难定位 bug。
为了不这样的错误,JavaScript 强制开发者在构造函数中先调用 super
,才能使用this
。这一限制,也被应用到了 React 组件:
constructor(props) {
super(props);
// ✅ 如今能够用 `this` 啦
this.state = { isOn: true };
}
复制代码
问题又来了:为何要传入 props
呢?
你可能觉得必须给 super
传入 props
,不然 React.Component
就无法初始化 this.props
:
// Inside React
class Component {
constructor(props) {
this.props = props;
// ...
}
}
复制代码
en...离真相不远 —— 事实上,React 也的确这么干了。
不过,若是你不当心漏传了 props
,直接调用了 super()
,你仍然能够在 render
和其余方法中访问 this.props
(不信的话能够试试嘛)。
为啥这样也行?由于React 会在构造函数被调用以后,会把 props
赋值给刚刚建立的实例对象:
// Inside React
const instance = new YourComponent(props);
instance.props = props;
复制代码
props
不传也能用,是有缘由的。
React 添加对 class 支持的时候,不只仅要支持 ES6 的 class,还须要考虑其余的 class 实现, CoffeeScript, ES6, Fable, Scala.js, TypeScript 中 class 的使用方式并不一致。因此,即便有了 ES6 class,在调用 super()
这个问题上,React 没作太多限制。
但这意味着你在使用 React 时,能够用 super()
代替 super(props)
了么?
别这么干,由于会带来其余问题。 虽然 React 会在构造函数运行以后,为 this.props
赋值,但在 super()
调用以后与构造函数结束以前, this.props
仍然是无法用的。
// Inside React
class Component {
constructor(props) {
this.props = props;
// ...
}
}
// Inside your code
class Button extends React.Component {
constructor(props) {
super(); // 😬 忘了传入 props
console.log(props); // ✅ {}
console.log(this.props); // 😬 undefined
}
// ...
}
复制代码
要是构造函数中调用了某个访问 props
的方法,那这个 bug 就更难定位了。所以我强烈建议始终使用super(props)
,即便这不是必须的:
class Button extends React.Component {
constructor(props) {
super(props); // ✅ We passed props
console.log(props); // ✅ {}
console.log(this.props); // ✅ {}
}
// ...
}
复制代码
上面的代码确保 this.props
始终是有值的。
还有一个问题可能困扰 React 开发者好久了。你应该已经注意到,当你在 class 中使用 Context API 时(不管是以前的 contextTypes
仍是如今的 contextType
API),context
都是做为构造函数的第二个参数。
咱们为何不用写 super(props, context)
?咱们固然能够这么写,不过 context API 用的相对较少,因此引起的 bug 也比较少。
感谢class 属性提案 ,这样的 bug 几近绝迹。只要没有显式声明构造函数,全部参数都会被自动传递。因此,在state = {}
表达式中,你能够访问this.props
以及 this.context
。