React, ES6 - getInitialState foi definido em uma classe JavaScript simples

115

Tenho o seguinte 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, tudo estava bem, agora estou recebendo 1 erro, 1 aviso e tenho uma pergunta de acompanhamento:

Erro: TypeError não capturado: não é possível ler a propriedade 'otherChecked' de nulo

Aviso: getInitialState foi definido em RadioOther, uma classe JavaScript simples. Isso só é compatível com classes criadas usando React.createClass. Você quis dizer definir uma propriedade de estado em vez disso?


  1. Alguém pode ver onde está o erro, eu sei que é devido à instrução condicional no DOM, mas aparentemente não estou declarando seu valor inicial corretamente?

  2. Devo tornar getInitialState estático

  3. Onde é o local apropriado para declarar meus proptypes se getInitialState não estiver correto?

ATUALIZAR:

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

produz "Uncaught ReferenceError: this is not defined", e enquanto abaixo corrige isso

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

mas agora, clicar no outro botão agora produz o erro:

Uncaught TypeError: Cannot read property 'props' of undefined

Talha Awan
fonte
1
A outra mudança para as classes ES6 é que os métodos não são "vinculados automaticamente" à instância, ou seja, quando você passa uma função como onChange={this.onRadChange}, thisnão se refere à instância quando onRadChangeé chamada. Você precisa retornos de chamada de bind em renderou fazê-lo no construtor: onChange={this.onRadChange.bind(this)}.
Ross Allen

Respostas:

239
  • getInitialStatenão é usado nas classes ES6. Em vez disso, atribua this.stateno construtor.
  • propTypes deve ser uma variável de classe estática ou atribuída à classe, não deve ser atribuída a instâncias de componentes.
  • Os métodos de membro não são "vinculados automaticamente" nas classes ES6. Para métodos usados ​​como retornos de chamada, use inicializadores de propriedade de classe ou atribua instâncias vinculadas no construtor.
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 = () => {
    ...
  };

  ...

}

Veja mais na documentação do React sobre classes ES6: convertendo uma função em uma classe

Ross Allen
fonte
Um ano depois, você pode descobrir que obtém: Uncaught TypeError: Cannot read property 'bind' of undefinederro Alguma ideia?
Jamie Hutber de
@JamieHutber Você pode criar uma nova pergunta com seu código? Se o método for definido na classe, não sei por que você obteria esse erro.
Ross Allen
Obrigado pela sugestão, @KennethWorden. Abri uma edição para os documentos do React: github.com/facebook/react/issues/7746
Ross Allen
'Prendê-los no lugar' significa usar uma função de seta como mostrado acima? Isso funciona, é claro. Oh, javascript!
jomofrodo
@jomofrodo Isso é ambíguo, obrigado por apontar isso. Eu quis dizer que rendervocê poderia fazer algo assim onClick={this.doFoo.bind(this)}, mas intencionalmente não incluí no código porque essa é a maneira menos eficiente de vincular, já que renderpode ser chamado com frequência e criar novas funções em cada chamada. Vou remover esse idioma da resposta.
Ross Allen
4

Somando-se à resposta de Ross .

Você também pode usar a nova função de seta ES6 na propriedade onChange

É funcionalmente equivalente a definir this.onRadChange = this.onRadChange.bind(this);no construtor, mas é mais conciso na minha opinião.

No seu caso, o botão de opção será semelhante ao mostrado abaixo.

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

Atualizar

Este método "mais conciso" é menos eficiente que as opções mencionadas na resposta de @Ross Allen porque gera uma nova função cada vez que o render()método é chamado

Sudarshan
fonte
1
Essa solução é funcionalmente correta, mas cria uma nova função em cada chamada para render(que pode ser chamada várias vezes). Usando um inicializador de propriedade de classe ou vinculação no construtor, apenas uma nova função associada é criada na construção do componente e nenhuma nova função é criada durante render.
Ross Allen
1

Se estiver usando babel-plugin-transform-class-properties ou babel-preset-stage-2 (ou stage-1 ou stage-0), você pode usar esta sintaxe:

class RadioOther extends React.Component {

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

  state = {
      otherChecked: false,
  };

  onRadChange = () => {
    ...
  };

  ...

}
lagos são
fonte