Stan komponentów React.js

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

Przyszedł czas na poznanie tajemniczego state w React.js. Udało nam się tworzyć komponenty, które pięknie wyświetlały przekazane propsy, ale trzeba przyznać szczerze: Bez wewnątrznego stanu nie da się zbudować funkcjonalnej aplikacji. Dzisiaj nauczysz się taki stan dodawać i wykorzystywać 🙂

Zacznijmy może od typowego przykładu powielonego w wielu kursach: Licznika. Stwórz komponent, który ma dwa przyciski (plus i minus) oraz output na wyświetlanie wyniku. Początkowo wartość wynosi 0, kliknięcie w przycisk odpowiednio zwiększa lub zmniejsza liczbę. Umiesz już obsłużyć kliknięcia, potrafisz też wyświetlać dane. Jak jednak je modyfikować?

Propsy są niemutowalne

Propsów nie da się zmienić z wnętrza komponentu. A jeśli spróbujesz to pewnie Ci się uda, ale będziesz mieć ogromne problemy — niespójne dane na ekranie, a może nawet jakieś błędy. Generalnie: Straszne rzeczy. Co do zasady: Propsów nie zmieniamy z wnętrza komponentu, do którego zostały one przekazane. I kropka.

Wchodzi state

A więc tutaj pojawia się słynny state. Do czego służy? Do przechowywania stanu komponentu. Ponadto, state można mutować dzięki funkcji setState. A więc jest to dokładnie ten brakujący element układanki, którego poszukujemy! Tak, tak, to właśnie w state będziesz przechowywać licznik, który chcesz zaimplementować.

Jeszcze jedna mała uwaga: Do state nie dobierzesz się w funkcyjnych komponentach. Stąd też ich nazwa: Stateless Functional Components. Potrzebna będzie klasa. Skoro to jest już jasne, weźmy się za pisanie kodu:

class App extends React.Component {
  render() {
    return (
      <div>
        <button>+</button>
        <output>{this.state.counter}</output>
        <button>-</button>
      </div>
    );
  }
}

Tak mniej-więcej będzie wyglądała nasza funkcja render. Jednak jeśli teraz odpalisz ten kod to dostaniesz w konsoli wyjątek, coś podobnego do Cannot read property 'counter' of null. Chwila drapania się po głowie i… no jasne, przecież nigdzie nie podaliśmy czym w ogóle jest state! Do tego potrzebny nam będzie konstruktor klasy. Dopisz na początku swojego komponentu:

  constructor() {
    super();
    this.state = {counter: 0};
  }

Przypomnę tylko, że jeśli klasa po czymś dziedziczy (tak jak tutaj po React.Component) to wewnątrz konstruktora musisz wywołać super(). Potem ustawiasz state na taki, jaki ma on być domyślnie — zanim zostaną wykonane jakiekolwiek akcje przez użytkownika. Teraz aplikacja renderuje się poprawnie, aczkolwiek nic spektakularnego się jeszcze nie dzieje!

this w React

Dopisujemy dwa onClick do przycisków i dwie metody w klasie: Jedna do zwiększania, a druga do zmniejszania wartości w liczniku. Posłuży do tego funkcja setState, w której odpowiednio ustawiamy licznik na (obecna wartość + 1) lub (obecna wartość - 1):

<button onClick={this.increment}>+</button>
  increment() {
    this.setState({
      counter: this.state.counter + 1
    })
  }

Jednak po kliknięciu w przycisk dostajemy tylko błąd: Cannot read property 'setState' of undefined. Cooo?

Wspominałem, że przy klasach pojawi nam się błąd związany z this. Każdy kto zna JS widzi już w czym problem: this w momencie wywołania funkcji increment nie jest związane z instancją komponentu. Jak rozwiązać ten problem?

Jest kilka sposobów, które omówię później. Na razie weźmiemy najprostszy: bind. Zmień kod w JSX:

<button onClick={this.increment.bind(this)}>+</button>

Woah, działa!

bind jest najprostszym rozwiązaniem, ale wcale nie najlepszym. Sam może stwarzać problemy, np. z wydajnością albo z tym, że referencja do funkcji się za każdym razem zmienia… Powrócę do tego tematu wkrótce.

Demo

Nauczyłaś/eś się używać state w React.js. Na razie w prosty sposób, ale ten temat jeszcze rozwinę w kolejnym wpisie. Tymczasem demo:

See the Pen Stan komponentów React.js by Michał Miszczyszyn (@mmiszy) on CodePen.

Ćwiczenie

Ćwiczenie: Dodaj dwa nowe liczniki. Pierwszy, który będzie zliczał wszystkie kliknięcia w przyciski (tzn. kliknięcie w + i - daje 0 na obecnym liczniku oraz 2 na nowym liczniku), oraz drugi, który będzie zliczał podwójne kliknięcia (tzw. double click) na elemencie z wynikiem. Jak wygląda teraz Twój state? Czy napotkałaś/eś jakieś problemy, albo coś Cię zaskoczyło? Napisz o tym w komentarzu 🙂