Metody cyklu życia komponentu w React.js

Ten wpis jest 10 częścią z 39 w kursie React.js
wp-content/uploads/2017/10/React_logo_wordmark-300x101-1-e1508612391308.png
  1. React.js: Wprowadzenie do kursu 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: globalny store i jednokierunkowy przepływ danych
  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

Przy okazji omawiania komponentów będących klasami w React.js, wspomniałem też o możliwości korzystania z metod cyklu życia (lifecycle methods). Chciałbym do tego tematu teraz powrócić. Pokażę jakie metody cyklu życia definiuje React.js, do czego one służą i kiedy mogą się przydać.

Cykl życia komponentu

Bardzo dokładnie omawiamy cykl życia komponentów na szkoleniach. Poznaj React w dwa dni na szkoleniu Type of Web!

Każdy komponent ma pewien określony cykl życia. Na odpowiednich etapach wywoływane są też jego metody cyklu życia. Możemy je podzielić ogólnie na dwie grupy:

  • z nazwami zaczynającymi się od will — wywoływane zanim się coś wydarzy
  • z nazwami zaczynającymi się od did — wywoływane po tym jak coś się zdarzyło

Oto kolejne etapy życia komponentów:

Montowanie

Komponent jest tworzony i dodawany do drzewa Dom.

  • constructor(props) — jeśli definiujesz konstruktor to nie zapomnij wywołać w nim super(props) na samym początku; możesz też tam ustawić this.state = {…} bez konieczności używania setState. To będzie początkowy stan aplikacji. Inne operacje (efekty uboczne, subskrypcje) powinny się znaleźć w componentDidMount
  • componentWillMount() — wywoływany tuż przed zamontowaniem komponentu; ta metoda raczej Ci się nie przyda
  • render() — funkcja, która powinna zwrócić jeden z możliwych wyników:
    • element (JSX)
    • tablicę elementów (JSX)
    • string lub liczbę
    • null (nic się nie renderuje)
    • boolean (nic się nie renderuje)
    • Portal (stworzony przez ReactDOM.createPortal(…))
  • componentDidMount() — wywoływana po zamontowaniu komponentu; to dobre miejsce na jakiekolwiek funkcje polegające na DOM lub na subskrypcje (nie zapomnij o usunięciu subskrypcji w componentWillUnmount()!)

Aktualizacja

Update może zostać wywołany gdy zmieniają się props lub state.

  • componentWillReceiveProps(nextProps) — wywoływana m.in. gdy zmieniają się propsy (np. gdy element-rodzic je zmienia); warto porównać this.props z nextProps i sprawdzić czy rzeczywiście coś się zmieniło (bo nie zawsze musi…)
  • shouldComponentUpdate(nextProps, nextState) — wywoływana zawsze przed render(); jeśli z tej funkcji zwrócisz false to render() nie zostanie wykonany, a widok się raczej nie przerenderuje — można to wykorzystać do optymalizowania aplikacji; UWAGA: komponenty-dzieci nadal mogą się przerenderować np. gdy zmieni się ich state, a w przyszłości React będzie traktował funkcję  shouldComponentUpdate() tylko jako wskazówkę, a nie wyznacznik — wrócę do tego tematu jeszcze
  • componentWillUpdate(nextProps, nextState) — wywoływana tuż przed render() — pamiętaj aby nie modyfikować state wewnątrz tej funkcji
  • render() — j.w.
  • componentDidUpdate(prevProps, prevState) — wywoływana od razu po renderze; dobre miejsce na zmiany w DOM (jeśli takowe są potrzebne)

Odmontowanie

Wywoływane gdy komponent jest usuwany z DOM

  • componentWillUnmount() — wywoływana przed usunięciem komponentu z DOM; dobre miejsce na „posprzątanie” po sobie — usunięcie timerów, subskrypcji, zmian w DOM itd.

Łapanie błędów

  • componentDidCatch(error, info) — wywoływana gdy pojawi się błąd w czasie renderowania, wywoływania metod cyklu życia lub w konstruktorze — zagnieżdżonych komponentów; błędy w samym komponencie nie są tutaj łapane (zostaną złapane w komponencie-rodzicu)

Przykład

Oto prosta aplikacja, która pokazuje ważny przykład używania metod cyklu życia. Są to dwa komponenty, App odpowiada za pokazywanie i ukrywanie (po kliknięciu w przycisk) drugiego komponentu. Drugi komponent to Date, który co sekundę aktualizuje stan i wyświetla aktualną datę i godzinę. Wykorzystane zostały takie lifecycle methods:

  • constructor() — ustawianie początkowego stanu
  • componentDidMount() — rozpoczęcie odliczania (setInterval)
  • componentWillUnmount() — usunięcie odliczania (clearInterval)

Warto też zwrócić uwagę na sposób w jaki komponent App renderuje komponent Date w zależności od this.state.dateVisible:

<div>
  {this.state.dateVisible && <DateComponent />}
</div>

See the Pen Metody cyklu życia komponentu w React.js by Michał Miszczyszyn (@mmiszy) on CodePen.

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

Ćwiczenie

Ćwiczenie: Stwórz dwa komponenty (rodzic i dziecko). Oto wymagania:

  1. Rodzic pozwala na ustawienie (input + button + onClick+ setState) jakiejś wartości liczbowej i przekazuje ją do dziecka jako props.
  2. Dziecko ma początkowo wyświetlać liczbę podaną od rodzica, a dodatkowo ma umożliwiać zwiększanie i zmniejszanie tej liczby (button + onClick + setState).
  3. W momencie gdy rodzic ustawi liczbę, dziecko powinno zresetować swój stan do podanej liczby (componentWillReceiveProps).

Aktualizacja 11.02.2019 r.: Przekreśliłem zadania powyżej. Miały one wyłącznie charakter dydaktyczny w celu poznania metody cyklu życia componentWillReceiveProps, ale wiele osób bardzo je sobie zapamiętało i stosowało podobny wzorzec w swoich aplikacjach. Poza tym React oznaczył tę metodę jako przestarzałą i niezalecaną. Zajrzyj do tego wpisu:

Nowe metody cyklu życia: getDerivedStateFromProps i getSnapshotBeforeUpdate

Sugerowane tutaj przeze mnie rozwiązanie było błędne. Prawidłowym rozwiązaniem problemu powyżej jest przeniesienie stanu dziecka wyżej — do rodzica (tzw. lifting state up). W ten sposób rodzic kontroluje stan obu inputów, a dziecko informuje go o zmianach, które chce wprowadzić. Dzięki temu unika się kilku częstych problemów: z rerenderem (który mógłby prowadzić do skasowania się wartości z inputów), z koniecznością wykrywania zmian (przez niezalecany componentWillReceiveProps), czy choćby z niejasnym przepływem danych (dane do dziecka od rodzica spływają w dół, ale tylko czasem… a dziecko nigdy nie informuje rodzica o zmianach — to niedobrze!). Oto zaktualizowana treść ćwiczenia:

Zaktualizowane ćwiczenie

Ćwiczenie: Stwórz dwa komponenty (rodzic i dziecko). Oto wymagania:

  1. Rodzic pozwala na ustawienie w inpucie jakiejś wartości liczbowej i przekazuje ją do dziecka.
  2. Dziecko ma początkowo wyświetlać liczbę podaną od rodzica, a dodatkowo ma umożliwiać zwiększanie i zmniejszanie tej liczby.
  3. W momencie, gdy rodzic ustawi liczbę, dziecko powinno zresetować swój stan do podanej liczby.
  4. Podpowiedź: Zastosuj tutaj lifting state up i stan obu komponentów trzymaj w rodzicu.

Napisz w komentarzu czy się udało. A jeśli masz jakieś wątpliwości albo nawet nie wiesz jak zacząć — odezwij się! Pomogę!

Nawigacja po kursie:
  1. React.js: Wprowadzenie do kursu 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: globalny store i jednokierunkowy przepływ danych
  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