React, ES6 - getInitialState se definió en una clase de JavaScript simple

115

Tengo el siguiente componente ( 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>
         )
     }
 };

Antes de usar ECMAScript6 todo estaba bien, ahora recibo 1 error, 1 advertencia y tengo una pregunta de seguimiento:

Error: no detectado TypeError: no se puede leer la propiedad 'otherChecked' de null

Advertencia: getInitialState se definió en RadioOther, una clase de JavaScript simple. Esto solo es compatible con las clases creadas con React.createClass. ¿Quería definir una propiedad estatal en su lugar?


  1. ¿Alguien puede ver dónde se encuentra el error, sé que se debe a la declaración condicional en el DOM, pero aparentemente no estoy declarando su valor inicial correctamente?

  2. ¿Debo hacer getInitialState estático?

  3. ¿Dónde está el lugar apropiado para declarar mis proptypes si getInitialState no es correcto?

ACTUALIZAR:

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

   module.exports = RadioOther;

@ssorallen, este código:

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

produce "Uncaught ReferenceError: this is not defined", y mientras que a continuación corrige que

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

pero ahora, hacer clic en el otro botón ahora produce un error:

Uncaught TypeError: Cannot read property 'props' of undefined

Talha Awan
fuente
1
El otro cambio para las clases de ES6 es que los métodos no están "enlazados automáticamente" a la instancia, es decir, cuando pasa una función como onChange={this.onRadChange}, thisno se refiere a la instancia cuando onRadChangese llama. Es necesario que las devoluciones de llamada se unen en rendero hacerlo en el constructor: onChange={this.onRadChange.bind(this)}.
Ross Allen

Respuestas:

239
  • getInitialStateno se utiliza en las clases de ES6. En su lugar, asigne this.stateen el constructor.
  • propTypes debe ser una variable de clase estática o asignada a la clase, no debe asignarse a instancias de componentes.
  • Los métodos de miembro no están "enlazados automáticamente" en las clases de ES6. Para los métodos utilizados como devoluciones de llamada, utilice inicializadores de propiedad de clase o asigne instancias vinculadas en el constructor.
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 = () => {
    ...
  };

  ...

}

Vea más en la documentación de React sobre las clases ES6: Convertir una función en una clase

Ross Allen
fuente
Un año después y ahora puede encontrar que obtiene: Uncaught TypeError: Cannot read property 'bind' of undefinederror ¿Alguna idea?
Jamie Hutber
@JamieHutber ¿Puedes crear una nueva pregunta con tu código? Si el método está definido en la clase, no sé por qué obtendría ese error.
Ross Allen
Gracias por la sugerencia, @KennethWorden. Abrí un problema para los documentos de React: github.com/facebook/react/issues/7746
Ross Allen
¿"Unirlos en su lugar" significa usar una función de flecha como se muestra arriba? Eso funciona, por supuesto. ¡Oh, javascript!
jomofrodo
@jomofrodo Eso es ambiguo, gracias por señalarlo. Quise decir renderque podría hacer algo como onClick={this.doFoo.bind(this)}, pero no lo incluí intencionalmente en el código porque esa es la forma menos eficiente de vincular, ya que renderse puede llamar a menudo y crearía nuevas funciones en cada llamada. Eliminaré ese lenguaje de la respuesta.
Ross Allen
4

Añadiendo a la respuesta de Ross .

También puede usar la nueva función de flecha ES6 en la propiedad onChange

Es funcionalmente equivalente a definir this.onRadChange = this.onRadChange.bind(this);en el constructor, pero es más conciso en mi opinión.

En su caso, el botón de opción se verá como el siguiente.

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

Actualizar

Este método "más conciso" es menos eficiente que las opciones mencionadas en la respuesta de @Ross Allen porque genera una nueva función cada vez render()que se llama al método

Sudarshan
fuente
1
Esta solución es funcionalmente correcta, pero crea una nueva función en cada llamada a render(que podría llamarse muchas veces). Al usar un inicializador de propiedad de clase o un enlace en el constructor, solo se crea una nueva función vinculada en la construcción del componente y no se crean nuevas funciones durante render.
Ross Allen