React, ES6 - getInitialState был определен в простом классе JavaScript

115

У меня есть следующий компонент ( 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>
         )
     }
 };

До использования ECMAScript6 все было хорошо, теперь я получаю 1 ошибку, 1 предупреждение, и у меня есть следующий вопрос:

Ошибка: Uncaught TypeError: невозможно прочитать свойство otherChecked с нулевым значением

Предупреждение: getInitialState был определен в RadioOther, простом классе JavaScript. Это поддерживается только для классов, созданных с помощью React.createClass. Вы хотели вместо этого определить государственную собственность?


  1. Может ли кто-нибудь увидеть, в чем заключается ошибка, я знаю, что это связано с условным выражением в DOM, но, по-видимому, я неправильно объявляю его начальное значение?

  2. Должен ли я сделать getInitialState статическим

  3. Где подходящее место для объявления моих проптипов, если getInitialState неверен?

ОБНОВИТЬ:

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

   module.exports = RadioOther;

@ssorallen, этот код:

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

производит "Uncaught ReferenceError: this is not defined", а ниже исправляет это

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

но теперь нажатие другой кнопки вызывает ошибку:

Uncaught TypeError: Cannot read property 'props' of undefined

Тальха Аван
источник
1
Другое изменение для классов ES6 заключается в том, что методы не «автоматически привязываются» к экземпляру, то есть, когда вы передаете такую ​​функцию, как onChange={this.onRadChange}, thisона не обращается к экземпляру при onRadChangeвызове. Вы должны связать обратные вызовы в renderили сделать это в конструкторе: onChange={this.onRadChange.bind(this)}.
Росс Аллен

Ответы:

239
  • getInitialStateне используется в классах ES6. Вместо этого назначьте this.stateв конструкторе.
  • propTypes должна быть статической переменной класса или назначаться классу, она не должна назначаться экземплярам компонентов.
  • Методы-члены не «привязаны автоматически» в классах ES6. Для методов, используемых в качестве обратных вызовов, используйте инициализаторы свойств класса или назначьте связанные экземпляры в конструкторе.
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 = () => {
    ...
  };

  ...

}

См. Дополнительную информацию в документации React о классах ES6: преобразование функции в класс

Росс Аллен
источник
Год спустя вы можете обнаружить, что получаете: Uncaught TypeError: Cannot read property 'bind' of undefinederror Любые идеи?
Jamie Hutber
@JamieHutber Можете ли вы создать новый вопрос с помощью вашего кода? Если метод определен в классе, я не знаю, почему вы получили эту ошибку.
Росс Аллен
Спасибо за предложение, @KennethWorden. Я открыл проблему для документации React: github.com/facebook/react/issues/7746
Росс Аллен
Означает ли «привязать их на месте» использование стрелочной функции, как показано выше? Конечно, это работает. Ой, JavaScript!
jomofrodo
@jomofrodo Это неоднозначно, спасибо, что указали на это. Я имел в виду, что renderвы можете сделать что-то подобное onClick={this.doFoo.bind(this)}, но я намеренно не включил это в код, потому что это наименее эффективный способ привязки, поскольку он renderможет вызываться часто и создавать новые функции при каждом вызове. Я уберу этот язык из ответа.
Росс Аллен
4

Добавление к ответу Росс .

Вы также можете использовать новую стрелочную функцию ES6 в свойстве onChange

Функционально это эквивалентно определению this.onRadChange = this.onRadChange.bind(this);в конструкторе, но, на мой взгляд, более лаконично.

В вашем случае переключатель будет выглядеть так, как показано ниже.

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

Обновить

Этот «более краткий» метод менее эффективен, чем параметры, упомянутые в ответе @Ross Allen, потому что он генерирует новую функцию при каждом render()вызове метода.

Sudarshan
источник
1
Это решение является функционально правильным, но оно создает новую функцию при каждом вызове render(которая может вызываться много раз). Используя инициализатор свойства класса или привязку в конструкторе, при построении компонента создается только одна новая привязанная функция, и во время нее новые функции не создаются render.
Росс Аллен
1

Если вы используете babel-plugin-transform-class-properties или babel-preset-stage-2 (или stage-1, или stage-0), вы можете использовать этот синтаксис:

class RadioOther extends React.Component {

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

  state = {
      otherChecked: false,
  };

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

  ...

}
lakesare
источник