【翻译】为什么我们需要写super(props)

原文链接Why Do We Write super(props)?

我听说Hooks是最近的热点。讽刺的是,我将从类组件的有趣之处开始这篇博客。这也不错吧!

对于高效地使用React来说,这些陷阱并不是非常重要。但是如果你想更加深入地了解内部的工作机制,了解这些会是非常有趣的事情。

第一个!

我在日常中写的 super(props) 远比我想象的要多的多。

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

当然,类领域的提案 让我们能跳过这些不必要的套路。

class Checkbox extends React.Component {
  state = { isOn: true };
  // ...
}

React 0.13 在2015年增加了对普通类的支持的时候,这样的语法已经被引入计划。 在类领域提供一个更加人性化的替代方案之前,声明 constructor 和调用 super(props) 一直是一个暂时的解决方案。

但是,让我们只使用 ES2015 的特性来看我们之前的例子。

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

为什么我们需要调用 super?我们能不调用它吗?如果我们必须要调用它,没有传入 props 参数会发生什么?还有其他参数吗?让我们来看看。

在 JavaScript中,super 指向符类的构造函数。(在我们举的例子中,它指向 React.Component 实现。)

重要的是,只有当你调用父构造函数之后,你才能够在 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 强制在我们在使用 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.greetColleaguessuper() 调用前使用,这导致我们有可能访问 this.name。但是这里 this.name 还没有声明!正如你所看到的,类似这样的代码很难考虑到。

为了避免这种陷阱,JavaScript强制你在 constructor 中使用 this 之前必须先调用 super。这会让父类做一些事情。而且,React 组件如果使用类定义也有同样的限制:

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

这里我们可能有另一个困惑:为什么要传入 props 参数?

你可能会认为对于 React.Component 构造函数初始化 this.props ,传入 propssuper 是必要的:

// Inside React
class Component {
  constructor(props) {
    this.props = props;
    // ...
  }
}

这离真正的答案也不远了——事实上,这就是它所做的

但有的时候,即使你没有给 super() 传入 props 参数,你仍然能够在 render 等生命周期中访问到 this.props。(如果你不信,自己试试吧!)

为什么这能成功呢?原因是 React 在你调用构造函数之后,立即将 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。但是在 super 调用和constructor执行结束之前,this.props 仍然是 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 中添加的现代 contextTypes),context 是作为第二个参数传入 constructor。

那么,为什么我们不写成 super(props, context) 呢?我们是可以的,但是 context 使用的更少,因此这个陷阱并不经常出现。

借助类领域的提案,这些陷阱可能大部分都不会在出现。不借助显式 constructor,所有参数会自动被传递。这也是为什么允许 state = {} 这类表达式包含对 this.props 或者 this.context 的引用。

借助 Hooks,我们甚至不再使用 superthis。但是这是以后的话题了。


 上一篇
HSTS VS 301 redirect HSTS VS 301 redirect
之前基于nginx为网站配置了HTTPS服务,配置过程中涉及到两个知识点: 301 永久重定向 HTST(HTTP Transport Security Protocol) 本文主要是通过梳理一下相关概念,理清这两种配置的目的。 H
2019-05-28
下一篇 
React项目实战(四)滚动的数字 React项目实战(四)滚动的数字
需求分析:我们要实现一个数字滚动效果,保证每个位上的数字都至少滚动一周,滚动效果是自下往上。让我们先思考一下几个细节:① 方案的选取:css?js?② 滚动的内容是什么?如果使用css动画用什么实现,如果使用js用什么实现?③ 怎么实现每个
2019-05-01
  目录