Stan komponentów React.js

Ten wpis jest 8 częścią z 39 w kursie React.js
wp-content/uploads/2017/10/React_logo_wordmark-300x101-1-e1508612391308.png
  1. Wprowadzenie do kursu React.js od podstaw
  2. Poznaj React.js
  3. Pierwszy komponent w React.js
  4. Props czyli atrybuty w React.js
  5. Podział na komponenty w React.js
  6. Klasy jako komponenty React.js
  7. Interakcja z komponentami React.js
  8. Stan komponentów React.js
  9. State w React.js 2
  10. Metody cyklu życia komponentu w React.js
  11. React.js w przykładach: filtrowanie statycznej listy
  12. Tworzenie aplikacji React.js dzięki create-react-app
  13. React.js na GitHub Pages dzięki create-react-app
  14. Testowanie aplikacji React.js — podstawy Enzyme
  15. Testowanie React.js w Enzyme — props, state i interakcje
  16. Poprawne bindowanie funkcji w React.js
  17. Odpowiadam na pytania: Babel, ECMAScript, destrukturyzacja, onClick, className
  18. Komunikacja pomiędzy komponentami w React.js
  19. Komunikacja z API w React.js
  20. Formularze w React.js — kontrolowane komponenty
  21. Formularze w React.js — niekontrolowane komponenty
  22. Odpowiadam na pytania: props, nawiasy klamrowe, funkcje vs klasy, import react
  23. TDD w React.js z pomocą react-testing-library
  24. Flux i Redux
  25. React + Redux — kurs: wprowadzenie i podstawy
  26. React + Redux — filtrowanie listy, proste selektory
  27. Projektowanie komponentów: Presentational & Container Components
  28. Asynchroniczność w Redux: redux-thunk
  29. Kiedy używać state, a kiedy Redux?
  30. Nowe metody cyklu życia: getDerivedStateFromProps i getSnapshotBeforeUpdate
  31. Leniwe ładowanie komponentów w React dzięki import
  32. Higher Order Reducers — Redux i powtarzanie kodu
  33. React Hooks — wprowadzenie i motywacja
  34. React Hooks: useState, czyli stan w komponentach funkcyjnych
  35. React Hooks: useState — wiele stanów, callbacki i inne niuanse
  36. React Hooks: useEffect — efekty uboczne w komponencie
  37. React Hooks a żądania do API
  38. useReducer — przenoszenie logiki poza komponent
  39. useMemo, useCallback, czyli rozwiązanie problemów ze zmieniającymi się propsami

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ć.

Wszystko na temat stanu wyjaśniamy na szkoleniach. Jeśli coś jest dla Ciebie niejasne to Naucz się React na szkoleniu Type of Web!

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 🙂

Nawigacja po kursie:
  1. Wprowadzenie do kursu React.js od podstaw
  2. Poznaj React.js
  3. Pierwszy komponent w React.js
  4. Props czyli atrybuty w React.js
  5. Podział na komponenty w React.js
  6. Klasy jako komponenty React.js
  7. Interakcja z komponentami React.js
  8. Stan komponentów React.js
  9. State w React.js 2
  10. Metody cyklu życia komponentu w React.js
  11. React.js w przykładach: filtrowanie statycznej listy
  12. Tworzenie aplikacji React.js dzięki create-react-app
  13. React.js na GitHub Pages dzięki create-react-app
  14. Testowanie aplikacji React.js — podstawy Enzyme
  15. Testowanie React.js w Enzyme — props, state i interakcje
  16. Poprawne bindowanie funkcji w React.js
  17. Odpowiadam na pytania: Babel, ECMAScript, destrukturyzacja, onClick, className
  18. Komunikacja pomiędzy komponentami w React.js
  19. Komunikacja z API w React.js
  20. Formularze w React.js — kontrolowane komponenty
  21. Formularze w React.js — niekontrolowane komponenty
  22. Odpowiadam na pytania: props, nawiasy klamrowe, funkcje vs klasy, import react
  23. TDD w React.js z pomocą react-testing-library
  24. Flux i Redux
  25. React + Redux — kurs: wprowadzenie i podstawy
  26. React + Redux — filtrowanie listy, proste selektory
  27. Projektowanie komponentów: Presentational & Container Components
  28. Asynchroniczność w Redux: redux-thunk
  29. Kiedy używać state, a kiedy Redux?
  30. Nowe metody cyklu życia: getDerivedStateFromProps i getSnapshotBeforeUpdate
  31. Leniwe ładowanie komponentów w React dzięki import
  32. Higher Order Reducers — Redux i powtarzanie kodu
  33. React Hooks — wprowadzenie i motywacja
  34. React Hooks: useState, czyli stan w komponentach funkcyjnych
  35. React Hooks: useState — wiele stanów, callbacki i inne niuanse
  36. React Hooks: useEffect — efekty uboczne w komponencie
  37. React Hooks a żądania do API
  38. useReducer — przenoszenie logiki poza komponent
  39. useMemo, useCallback, czyli rozwiązanie problemów ze zmieniającymi się propsami