React, ES6 - getInitialState zostało zdefiniowane na zwykłej klasie JavaScript

115

Mam następujący składnik ( 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>
         )
     }
 };

Przed użyciem ECMAScript6 wszystko było dobrze, teraz pojawia się 1 błąd, 1 ostrzeżenie i mam dodatkowe pytanie:

Błąd: Uncaught TypeError: Nie można odczytać właściwości „otherChecked” wartości null

Ostrzeżenie: getInitialState zostało zdefiniowane w RadioOther, zwykłej klasie JavaScript. Jest to obsługiwane tylko dla klas utworzonych za pomocą React.createClass. Czy zamiast tego chodziło Ci o zdefiniowanie własności państwowej?


  1. Czy ktoś może zobaczyć, gdzie leży błąd, wiem, że wynika to z instrukcji warunkowej w DOM, ale najwyraźniej nie deklaruję poprawnie jego wartości początkowej?

  2. Czy powinienem ustawić getInitialState jako statyczne

  3. Gdzie jest odpowiednie miejsce do zadeklarowania moich właściwości, jeśli getInitialState nie jest poprawne?

AKTUALIZACJA:

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

   module.exports = RadioOther;

@ssorallen, ten kod:

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

produkuje "Uncaught ReferenceError: this is not defined", a poniżej poprawia to

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

ale teraz kliknięcie drugiego przycisku powoduje teraz błąd:

Uncaught TypeError: Cannot read property 'props' of undefined

Talha Awan
źródło
1
Inną zmianą dla klas ES6 jest to, że metody nie są „automatycznie wiązane” z instancją, co oznacza, że ​​przekazanie funkcji takiej jak onChange={this.onRadChange}, thisnie odnosi się do instancji, gdy onRadChangejest wywoływana. Trzeba callbacków powiązań w renderlub zrobić go w konstruktorze: onChange={this.onRadChange.bind(this)}.
Ross Allen

Odpowiedzi:

239
  • getInitialStatenie jest używany w klasach ES6. Zamiast tego przypisz this.statew konstruktorze.
  • propTypes powinien być statyczną zmienną klasy lub przypisany do klasy, nie powinien być przypisywany do instancji komponentów.
  • Metody składowe nie są „wiązane automatycznie” w klasach ES6. W przypadku metod używanych jako wywołania zwrotne użyj inicjatorów właściwości klasy lub przypisz powiązane wystąpienia w konstruktorze.
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 = () => {
    ...
  };

  ...

}

Zobacz więcej w dokumentacji Reacta o klasach ES6: Converting a Function to a Class

Ross Allen
źródło
Rok później może się okazać, że otrzymujesz: Uncaught TypeError: Cannot read property 'bind' of undefinedbłąd Jakieś pomysły?
Jamie Hutber
@JamieHutber Czy możesz utworzyć nowe pytanie za pomocą swojego kodu? Jeśli metoda jest zdefiniowana w klasie, nie wiem, dlaczego miałbyś otrzymać ten błąd.
Ross Allen
Dzięki za sugestię @KennethWorden. Otworzyłem numer do dokumentów React: github.com/facebook/react/issues/7746
Ross Allen
Czy „zwiąż je w miejscu” oznacza użycie funkcji strzałki, jak pokazano powyżej? To oczywiście działa. Och, javascript!
jomofrodo
@jomofrodo To niejednoznaczne, dziękuję za zwrócenie uwagi. Miałem na myśli, renderże możesz zrobić coś podobnego onClick={this.doFoo.bind(this)}, ale celowo nie włączyłem tego do kodu, ponieważ jest to najmniej efektywny sposób wiązania, ponieważ rendermoże być często wywoływany i tworzył nowe funkcje przy każdym wywołaniu. Usunę ten język z odpowiedzi.
Ross Allen,
4

Dodając do odpowiedzi Rossa .

Możesz także użyć nowej funkcji strzałki ES6 we właściwości onChange

Funkcjonalnie odpowiada definiowaniu this.onRadChange = this.onRadChange.bind(this); w konstruktorze, ale moim zdaniem jest bardziej zwięzłe.

W twoim przypadku przycisk opcji będzie wyglądał jak poniżej.

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

Aktualizacja

Ta „bardziej zwięzła” metoda jest mniej wydajna niż opcje wymienione w odpowiedzi @Ross Allen, ponieważ generuje nową funkcję za każdym razem, gdy render()metoda jest wywoływana

Sudarshan
źródło
1
To rozwiązanie jest funkcjonalnie poprawne, ale tworzy nową funkcję przy każdym wywołaniu render (która może być wywoływana wiele razy). Używając inicjatora właściwości klasy lub powiązania w konstruktorze, tylko jedna nowa powiązana funkcja jest tworzona podczas konstruowania składnika i nie są tworzone żadne nowe funkcje podczas render.
Ross Allen,