Klasy jako komponenty React.js

Ten wpis jest 6 częścią z 8 w kursie React.js

Do tej pory używaliśmy prostych funkcji i z nich tworzyliśmy komponenty React. Mimo, że przeważająca część komponentów, które tworzę są właśnie takimi prostymi funkcjami, to jednak nie wszystkie. Funkcje są bardzo przydatne, jednak poniekąd ograniczone. Jak więc inaczej można tworzyć komponenty w React.js? Używając klas!

Stateless Functional Component

SFC, albo Stateless Functional Component — tak profesjonalnie nazywają się te komponenty, które do tej pory tworzyliśmy. Bezstanowe, funkcyjne — dokładnie takie one są 🙂 Spróbujmy przepisać SFC, które wcześniej stworzyliśmy na klasę:

function ContactItem({ login, name, department }) {
  const imgUrl = `https://api.adorable.io/avatars/55/${login}.png`;
  return (
    <li className="item">
      <img src={imgUrl} className="ui mini rounded image" />
      <div className="content">
        <h4 className="header">{name}</h4>
        <div className="description">{department}</div>
      </div>
    </li>
  );
}

Class w React.js

Korzystamy z klas znanych z ES2015. Klasa ta koniecznie musi dziedziczyć po React.Component (lub PureComponent — o tym kiedy indziej). Implementujemy w niej tylko jedną metodę: render. Oto kod poniżej:

class ContactItem extends React.Component {
  render() {
    const { login, name, department } = this.props
    const imgUrl = `https://api.adorable.io/avatars/55/${login}.png`;
    return (
      <li className="item">
        <img src={imgUrl} className="ui mini rounded image" />
        <div className="content">
          <h4 className="header">{name}</h4>
          <div className="description">{department}</div>
        </div>
      </li>
    );
  }
}

Widzisz jakieś znaczące różnice?

Po co Ci class w React.js?

Na razie nie widać żadnej przewagi klasy nad funkcją. I rzeczywiście — przy takich komponentach (prezentacyjnych) lepiej jest napisać funkcję niż klasę. Gdzie klasy wygrywają i co możemy z nimi zrobić?

Klasy nie istnieją bez powodu 🙂 Oto kilka możliwości. Wszystkie omówimy w kolejnych częściach tego kursu:

  • możliwość definiowania stanu komponentu (state), który sprawi, że Twoja aplikacja zacznie „żyć”
  • dostęp do metod cyklu życia komponentu (lifecycle methods), dzięki którym będziemy mogli reagować na różne wydarzenia
  • możliwość definiowania fragmentów komponentów (każda funkcja może zwracać JSX!) jako metod w klasie — poprawa czytelności kodu
  • możliwość tworzenia metod pomocniczych, z których można korzystać wewnątrz funkcji render. Na przykład do walidacji danych (przykład poniżej)

Przykładowo, jeśli korzystasz z jakiegoś modułu do formularzy w React, pewnie możesz napisać kod podobny do tego:

class MyForm extends React.Component {
  render() {
    return (
      <Input name="nip" validate={[this.validateInput]} />
    )
  }

  validateInput(value) {
    return value && value.length === 10;
  }
}

Co dalej?

Interakcje z komponentami. Poznasz też state oraz metody cyklu życia.

Jeśli chcesz na bieżąco śledzić kolejne części kursu React.js to koniecznie śledź mnie na Facebooku i zapisz się na newsletter.

Ćwiczenie

Ćwiczenie: Przepisz pozostałe komponenty na klasy. Czy napotkałeś/aś jakieś trudności? Napisz w komentarzu!

  • Tomasz Sochacki

    A jak masz ustawione u siebie reguły eslinta dla react z redux? Ostatnio zainteresowałem się Airbnb tylko mam zawiłostk, ponieważ tam preferowane są klasy tylko w przypadku komponentów „stanowych”. W redux natomiast nie operujemy na state tylko na propsach i tutaj ciekawi mnie jakie praktyki stosują inni „reactowcy”. Ja założylem sobie, że bezstanowe robię na funkcjach, ale już pobierające dane ze store robię na klasach, co daje mi przyjemniejszą składnię np. dla propTypes, defaultTypes (jako static). A co Ty myślisz?
    Pozdrawiam, a tak na marginesie to masz fajny styl pisania, myślę, że wiele osób zaczynających dopiero React łatwo zrozumie co i jak z Twoich wpisów, powodzenia!

    • Hej, dzięki za miłe słowa 🙂

      Co do Twoich pytań… zawsze kiedy jest potrzebna klasa to używam klasy 😉 Czyli wtedy gdy chcę mieć state, albo skorzystać z lifecycle methods. Poza tymi przypadkami praktycznie nie używam klas w ogóle.

      Nie korzystam też z propTypes, bo na codzień piszę w TypeScripcie, który to praktycznie całkowicie zastępuje.

      Jeśli chodzi o eslinta — mamy własną konfigurację teraz w projekcie. Ale zobacz co domyślnie generuje create-react-app — tego bym się pewnie trzymał 🙂 Chociaż w sumie to nieważne. Ważne, żeby być spójnym i robić zgodnie z innymi osobami z projektu.

      • Tomasz Sochacki

        Akurat w tym konkretnym projekcie mam tę przewagę, że to ja określam „reguły gry” dlatego chciałem poznać również inne opinie 🙂
        Do TS jakoś nie mogę się przekonać, choć parę razy się nad tym też zastanawiałem… ale pożyjemy zobaczymy.

  • Wiesław Turzański

    W czasie wykonywania zadania domowego zastanawiałem się jaka jest różnica pomiędzy użyciem takich składni:

    import React, {Component} from 'react';
    class Klasa extends Component {
    ...
    }

    a


    import React from 'react';
    class Klasa extends React.Component {
    ...
    }

    a według mnie nie ma(?), to w związku z tym jest jakaś reguła?
    Pozdrawiam.

    • No w tym przypadku mam kilka uwag 🙂

      Po pierwsze, zapis import React from 'react' nie jest całkowicie poprawny. Jest możliwy i działa tylko z Babelem i tylko przy włączonym odpowiednim presecie! Wynika to z tego, że React nie jest napisany z użyciem modułów ES i nie ma export default.
      Prawidłowy import, biorąc pod uwagę istniejący kod reacta, wygląda tak:
      import * as React from 'react'
      Zadziała on zarówno z Babelem z presetem, bez presetów, a także w TypeScripcie.

      Po drugie, całkowicie poprawne jest użycie
      import { Component } from 'react'
      Nie ma w tym nic złego i wiele osób poleca taki zapis. Jednak, przynajmniej na razie, JSX wymaga, aby w danym scope’ie był zaimportowany React. Więc ostatecznie miałbyś takie dwie linijki:

      import * as React from 'react';
      import { Component } from 'react';

      Dla wielu osób nie ma to sensu, więc rezygnują z drugiego importu i zamiast tego używają React.Component

  • Wiesław Turzański

    Staram się być konsekwentny i podążać za kursem dokładnie lekcja za lekcją. Piszę kod w jednym pliku index.html. I do momentu definiowania komponentów jako funkcji było OK. Ale przy zamienianiu ich w klasy pojawiły się błędy przy próbie wyświetlenia pliku w przeglądarce. Sekcja head wygląda tak:

    ...

    .....

    gdy zamieniłem na:


    ...

    ...

    zadziałało?
    Poza tym gdy chcę zapisać (podzielić) komponenty do osobnych plików .js i wczytać je do html poprzez script tak nie działa.
    Ponieważ jest to kurs dla początkujących proszę o wskazówki lub wskazać miejsce gdzie będę mógł pogłębić swoją podstawową wiedzę.
    Pozdrawiam

    • A jaki konkretnie był problem wcześniej z klasami?

      • Wiesław Turzański

        Dziękuję za odpowiedź. Teraz rozumiem.
        Błąd:
        „Uncaught SyntaxError: Unexpected token <"

        Oprócz pliku html, w osobnym projekcie stworzonym poprzez, właśnie create-react-app, równolegle tworzę sobie aplikację web – podzieloną na poszczególne lekcje. Tam wszystko śmiga jak tra-la-la. 😉
        Pozdrawiam

  • Pytanie odnośnie samej konwencji tworzenia metod. W którymś eslincie spotkałem się z regułą, która nakazuje pisać metody dopiero wtedy kiedy korzystają z `this`, aby odróżniać metodę od funkcji która może zostać wyniesiona za klasę. Czy jest to zwykła konwencja i trzymanie spójności w projekcie czy kryje się za tym jakaś głębsza logika?
    Pozdrawiam 😀

    • Prawdę mówiąc nie spotkałem się z tą konwencją w kontekście Reactowych komponentów 🙂 Ja wszystkie funkcje zawieram w klasie. Korzystam z TypeScripta, więc oznaczam je jeszcze jako private 🙂

  • Bartek

    Czekamy czekamy 😉