• Why Do We Write super(props)?
  • By Dan Abramov
  • Translator: Washington Hua
  • I hear Hooks are hot these days. Ironically, I’d like to start this blog with some interesting stories about class components. How to! (Skin is very happy)

    These bugs won’t stop you from using React effectively, but if you’re willing to take a closer look at how it works, you’ll find them interesting.

    This is the first one.

    I’ve written a lot more super(props) in my life than I ever thought I would

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

    Of course, the Class Fields Proposal allows us to skip this ritual.

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

    This syntax was planned in 2015 when React 0.13 added support for pure classes. Defining constructor and calling super(props) was always a temporary solution before class Fiels came along.

    However, let’s review this example using only ES2015 features.

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

    Why do we call super? Can I not call it? If I had to call, what if I didn’t pass props? Any other parameters? Let’s take a look.

    In JavaScript, super refers to the constructor of the parent class. (In our case, it points to the React.Component implementation.)

    The point is that you can’t use this in the constructor until you call the parent constructor. JavaScript won’t let you do that.

    class Checkbox extends React.Component {
      constructor(props) {
        // 🔴 cannot use 'this' at this time
        super(props);
        // ✅ can start now
        this.state = { isOn: true };
      }
      // ...
    }Copy the code

    There’s a good reason why JavaScript forces you to run the superclass constructor before using this. Consider this class structure:

    class Person { constructor(name) { this.name = name; } } class PolitePerson extends Person { constructor(name) { this.greetColleagues(); // 🔴 this is not allowed. Super (name); }greetColleagues() {
        alert('Good morning folks! '); }}Copy the code

    Imagine if it was allowed to use this before calling super. In a month. We might change greetColleagues and add person’s name to the message.

    greetColleagues() {
      alert('Good morning folks! ');
      alert('My name is ' + this.name + ', nice to meet you! ');
    }Copy the code

    But we forgot that this.greetcolleagues () was called before super() had a chance to set this.name. This.name isn’t even defined yet! As you can see, code like this can be difficult to understand.

    To avoid such pitfalls, JavaScript forces you to call super first if you want to use only this in constructors. Let the parent do its job! This limitation also applies to React components defined as classes.

    constructor(props) { super(props); // ✅ can now use 'this' this.state = {isOn:true };
    }Copy the code

    This leaves us with another question: Why pass props?

    You might think it necessary to pass props to super, which allows the base class react.componentto initialize this.props:

    // React internal class Component {constructor(props) {this. Props = props; / /... }}Copy the code

    Pretty close — in fact, that’s what it does.

    However, even if you don’t pass the props argument when you call super(), you can still access this.props in render and other methods. (If you don’t believe me, try it yourself.)

    How does this work? React sets the props to the instance immediately after calling your constructor:

    // React inner const instance = new YourComponent(props); instance.props = props;Copy the code

    So even if you forget to pass props to super(), React will still set them afterwards. And for good reason.

    When React added support for classes, it didn’t just add ES6 support, but wanted to support the broadest possible Class abstraction. React deliberately doesn’t take a position on whether calling super() is necessary because it’s not sure which of ClojureScript, CoffeeScript, ES6, Fable, Scala.js, TypeScript, or any other solution is better for defining components.

    So does that mean you can just say super() without super(props)?

    Maybe not, because it’s still troubling. Sure, React will set this.props after your constructor runs. But this.props is undefined until the super call ends in the constructor.

    // React internal class Component {constructor(props) {this. Props = props; / /... }} // Your code class Button extends React.Com constructor(props) {super(); // 😬 we forgot to pass in props console.log(props); / / ✅ {} the console. The log (enclosing props); // 😬 undefined} //... }Copy the code

    If this happens to some function called from a constructor, it can be more cumbersome to debug. That’s why I recommend always using super(props), even if it’s not necessary:

    class Button extends React.Component { constructor(props) { super(props); // ✅ we are calling props console.log(props); / / ✅ {} the console. The log (enclosing props); // ✅ {}} //... }Copy the code

    This ensures that this.props is set even before the constructor returns.

    One final point long-time React users might be curious about.

    You may have noticed that when you use the Context API in a Class (either the old syntax or the new modern syntax in React 16.6), the Context is passed in as the second argument to the constructor.

    So why don’t we say super(props, context)? Of course we can do that, but the context isn’t used that often, so the trap doesn’t really matter that much.

    With the release of the Class Fields Proposal, this problem no longer exists. Even if the constructor is not explicitly called, all arguments are passed in automatically. This allows expressions like state = {} to reference this.props directly if necessary. Or enclosing context.

    In Hooks, we don’t even have super or this. We’ll talk about that another time.