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

3

58

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?

Sanjiv answered 13/6, 2016 at 11:14 Comment(3)
There is no bad or good, they are equivalent, its a matter of preferenceChurn
Class properties are a proposal. They are not part of ES7. Please read the tag description at least: ecmascript-7.Hypesthesia
A constructor is no longer required anymore https://mcmap.net/q/102632/-when-is-it-appropriate-to-use-a-constructor-in-reactHandle
R
74

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

Rota answered 13/6, 2016 at 11:18 Comment(2)
If the class extends React.Component, they don't transpile to the same thingDispirit
Yeah, fair enough, but the semantics of the this.state doesn't change.Rota
H
8

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 = () => ...;
Herbherbaceous answered 7/10, 2018 at 13:7 Comment(0)
P
1

Dan's code actually has a subtle bug which is why I recommend using the initializers whenever possible. React component constructors take two arguments - props and the context. He's not passing it to the parent constructor and it could easily be missed by some other developer who needed it.

Sometimes you have no choice, like when the initializer depends on the constructor arguments, so just remember to pass all the arguments to the parent.

After trying a few things, looks like React doesn't have the issue I was thinking of. You can pass whatever you want to the parent constructor and it will be fine. E.g.:

class MyComponent extends React.Component {
  constructor(props) {
    super({})
  }

  render() {
    // this.props will still be set correctly here
  }
}

I still recommend using the initializers as not having to call the parent constructor is one less thing to think about.

Phrasing answered 14/6, 2016 at 2:41 Comment(4)
I had no idea that React constructors took two arguments. What is the context?Sanjiv
It's a way to pass data to components deeper in the hierarchy without having to set props on all the intermediaries. I tried a few tests and looks like React doesn't have the issue I mentioned. Point is still valid, though.Phrasing
"You can pass whatever you want to the parent constructor and it will be fine" - NOPE. facebook.github.io/react/docs/react-component.html#constructor and also : facebook.github.io/react/docs/state-and-lifecycle.htmlIntertidal
Documentation doesn't always agree with the reality. Not saying that people should ignore the docs, just pointing out that React behaves differently in actuality. github.com/facebook/react/blob/…Phrasing

© 2022 - 2024 — McMap. All rights reserved.