web-dev-qa-db-fra.com

Est-il préférable de définir l'état dans le constructeur ou d'utiliser des initialiseurs de propriété?

Selon this documentation babel, la bonne façon d'utiliser ES6 + avec React consiste à initialiser des composants comme celui-ci:

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,
  }
}

Mais certains exemples officiels, comme le module React DnD de Dan Abramov, utilisent ES6 + mais définissent toujours l'état dans le constructeur:

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

Maintenant, Dan Abramov, étant un contributeur important à React, sait probablement qu'il peut définir un état en dehors du constructeur, mais opte toujours pour le faire au sein du constructeur.

Je me demande donc quelle est la meilleure voie et pourquoi?

53
abustamam

Je crois que c'est une question de préférence personnelle. La sortie transpilée est la même en termes de sémantique.

68
ctrlplusb

Ils sont équivalents car proposition de champ de classe est un sucre syntaxique pour le code du corps du constructeur.

Dans le cas où il n'y a pas besoin de constructeur explicite (création de variables locales temporaires, etc.), constructor peut être omis en faveur des champs de classe.

Le problème avec le constructeur explicite est que les arguments super (props) sont souvent omis par erreur, cela peut entraîner des problèmes:

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

Un constructeur explicite peut être bénéfique pour la lisibilité. Les méthodes sont conventionnelles placées sous constructor, même les propriétés des flèches. Cela ne fonctionnera pas comme prévu car les champs de classe sont affectés dans l'ordre dans lequel ils ont été répertoriés:

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

someMethod = () => ...;

Dans ce cas, le constructeur explicite peut entraîner un code plus lisible:

constructor(props) {
    super(props);

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

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

someMethod = () => ...;
8
Estus Flask

Le code de Dan a en fait un bug subtil, c'est pourquoi je recommande d'utiliser les initialiseurs autant que possible. React prennent deux arguments - props et le contexte. Il ne le passe pas au constructeur parent et il pourrait facilement être manqué par un autre développeur qui en aurait besoin.

Parfois, vous n'avez pas le choix, comme lorsque l'initialiseur dépend des arguments du constructeur, alors n'oubliez pas de passer tous les arguments au parent.

Après avoir essayé quelques choses, ressemble à React n'a pas le problème auquel je pensais. Vous pouvez passer ce que vous voulez au constructeur parent et ça ira. Par exemple:

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

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

Je recommande toujours d'utiliser les initialiseurs car ne pas avoir à appeler le constructeur parent est une chose de moins à penser.

1
Gunchars