React, ES6 - getInitialState a été défini sur une classe JavaScript simple

115

J'ai le composant suivant ( radioOther.jsx):

 'use strict';

 //module.exports = <-- omitted in update

   class RadioOther extends React.Component {

     // omitted in update 
     // getInitialState() {
     //    propTypes: {
     //        name: React.PropTypes.string.isRequired
     //    }
     //    return {
     //       otherChecked: false
     //   }
     // }

     componentDidUpdate(prevProps, prevState) {
         var otherRadBtn = this.refs.otherRadBtn.getDOMNode();

         if (prevState.otherChecked !== otherRadBtn.checked) {
             console.log('Other radio btn clicked.')
             this.setState({
                 otherChecked: otherRadBtn.checked,
             });
         }
     }

     onRadChange(e) {
         var input = e.target;
         this.setState({
             otherChecked: input.checked
         });
     }

     render() {
         return (
             <div>
                 <p className="form-group radio">
                     <label>
                         <input type="radio"
                                ref="otherRadBtn"
                                onChange={this.onRadChange}
                                name={this.props.name}
                                value="other"/>
                         Other
                     </label>
                     {this.state.otherChecked ?
                         (<label className="form-inline">
                             Please Specify:
                             <input
                                 placeholder="Please Specify"
                                 type="text"
                                 name="referrer_other"
                                 />
                         </label>)
                         :
                         ('')
                     }
                 </p>
             </div>
         )
     }
 };

Avant d'utiliser ECMAScript6, tout allait bien, maintenant je reçois 1 erreur, 1 avertissement et j'ai une question de suivi:

Erreur: Uncaught TypeError: Impossible de lire la propriété 'otherChecked' de null

Attention: getInitialState a été défini sur RadioOther, une classe JavaScript simple. Ceci n'est pris en charge que pour les classes créées à l'aide de React.createClass. Vouliez-vous plutôt définir une propriété d'état?


  1. Quelqu'un peut-il voir où se trouve l'erreur, je sais que c'est dû à l'instruction conditionnelle dans le DOM mais apparemment je ne déclare pas sa valeur initiale correctement?

  2. Dois-je rendre getInitialState statique

  3. Où est l'endroit approprié pour déclarer mes proptypes si getInitialState n'est pas correct?

METTRE À JOUR:

   RadioOther.propTypes = {
       name: React.PropTypes.string,
       other: React.PropTypes.bool,
       options: React.PropTypes.array }

   module.exports = RadioOther;

@ssorallen, ce code:

     constructor(props) {
         this.state = {
             otherChecked: false,
         };
     }

produit "Uncaught ReferenceError: this is not defined", et en dessous corrige que

     constructor(props) {
     super(props);
         this.state = {
             otherChecked: false,
         };
     }

mais maintenant, cliquer sur l'autre bouton produit maintenant une erreur:

Uncaught TypeError: Cannot read property 'props' of undefined

Talha Awan
la source
1
L'autre changement pour les classes ES6 est que les méthodes ne sont pas "liées automatiquement" à l'instance, ce qui signifie que lorsque vous passez une fonction comme onChange={this.onRadChange}, thisne fait pas référence à l'instance lorsqu'elle onRadChangeest appelée. Vous devez callbacks bind dans renderou le faire dans le constructeur: onChange={this.onRadChange.bind(this)}.
Ross Allen

Réponses:

239
  • getInitialStaten'est pas utilisé dans les classes ES6. Attribuez plutôtthis.state dans le constructeur.
  • propTypes doit être une variable de classe statique ou attribuée à la classe, elle ne doit pas être affectée aux instances de composant.
  • Les méthodes membres ne sont pas "liées automatiquement" dans les classes ES6. Pour les méthodes utilisées comme rappels, utilisez des initialiseurs de propriété de classe ou affectez des instances liées dans le constructeur.
export default class RadioOther extends React.Component {

  static propTypes = {
    name: React.PropTypes.string.isRequired,
  };

  constructor(props) {
    super(props);
    this.state = {
      otherChecked: false,
    };
  }

  // Class property initializer. `this` will be the instance when
  // the function is called.
  onRadChange = () => {
    ...
  };

  ...

}

Pour en savoir plus, consultez la documentation de React sur les classes ES6: Conversion d'une fonction en classe

Ross Allen
la source
Un an plus tard et maintenant vous pourriez trouver que vous obtenez: Uncaught TypeError: Cannot read property 'bind' of undefinederreur Des idées?
Jamie Hutber
@JamieHutber Pouvez-vous créer une nouvelle question avec votre code? Si la méthode est définie sur la classe, je ne sais pas pourquoi vous obtiendriez cette erreur.
Ross Allen
Merci pour la suggestion, @KennethWorden. J'ai ouvert un numéro pour les documents React: github.com/facebook/react/issues/7746
Ross Allen
Est-ce que «les lier en place» signifie utiliser une fonction de flèche comme vous le montrez ci-dessus? Cela fonctionne, bien sûr. Oh, javascript!
jomofrodo
@jomofrodo C'est ambigu, merci de l'avoir signalé. Je voulais dire que rendertu pourrais faire quelque chose commeonClick={this.doFoo.bind(this)} , mais je ne l'ai pas inclus intentionnellement dans le code parce que c'est le moyen le moins efficace de lier car il renderpeut être appelé souvent et créer de nouvelles fonctions à chaque appel. Je supprimerai cette langue de la réponse.
Ross Allen
4

Ajout à la réponse de Ross .

Vous pouvez également utiliser la nouvelle fonction de flèche ES6 sur la propriété onChange

C'est fonctionnellement équivalent à définir this.onRadChange = this.onRadChange.bind(this); dans le constructeur mais est plus concis à mon avis.

Dans votre cas, le bouton radio ressemblera à celui ci-dessous.

<input type="radio"
       ref="otherRadBtn"
       onChange={(e)=> this.onRadChange(e)}
       name={this.props.name}
       value="other"/>

Mettre à jour

Cette méthode "plus concise" est moins efficace que les options mentionnées dans la réponse de @Ross Allen car elle génère une nouvelle fonction à chaque fois que la render()méthode est appelée

Sudarshan
la source
1
Cette solution est fonctionnellement correcte, mais elle crée une nouvelle fonction à chaque appel à render(qui peut être appelée plusieurs fois). En utilisant un initialiseur de propriété de classe ou une liaison dans le constructeur, une seule nouvelle fonction liée est créée lors de la construction du composant et aucune nouvelle fonction n'est créée pendant render.
Ross Allen