Is it better to define state in constructor or using property initializers?

According to this babel documentation, the correct way to use ES6+ with React is to initial components like this:

class Video extends React.Component {
  static defaultProps = {
    autoPlay: false,
    maxLoops: 10,
  }
  static propTypes = {
    autoPlay: React.PropTypes.bool.isRequired,
    maxLoops: React.PropTypes.number.isRequired,
    posterFrameSrc: React.PropTypes.string.isRequired,
    videoSrc: React.PropTypes.string.isRequired,
  }
  state = {
    loopsRemaining: this.props.maxLoops,
  }
}

But some official examples, like Dan Abramov's own React DnD module, uses ES6+ but still defines state within the constructor:

constructor(props) {
    super(props);
    this.moveCard = this.moveCard.bind(this);
    this.state = {
       // state stuff
    }
}

Now Dan Abramov, being a significant contributor to React, probably knows that he can define state outside the constructor, yet still opts to do it within the constructor.

So I'm just wondering which way is better and why?


Solution 1:

I believe it's a matter of personal preference. The transpiled output is the same in terms of semantics.

  • Class Property
  • Constructor

Solution 2:

They are equivalent because class field proposal is syntactic sugar for constructor body code.

In case there's no need for explicit constructor (creating temporary local variables, etc), constructor can be omitted in favour of class fields.

The problem with explicit constructor is that super arguments (props) are often omitted by mistake, this may result in problems:

constructor() {
    super();
    this.state = { foo: this.props.foo } // this.props is undefined
}

Explicit constructor may beneficial for readability. Methods are conventional placed below constructor, even arrow properties. This won't work as intended because class fields are assigned in the order they were listed:

state = { foo: { method: this.someMethod } } // this.someMethod is undefined

someMethod = () => ...;

In this case explicit constructor may result in more readable code:

constructor(props) {
    super(props);

    // <-- this is the place where this.someMethod is really assigned

    this.state = { foo: { method: this.someMethod } }
}

someMethod = () => ...;