Cykl życia komponentu Reacta ma trzy fazy: montowanie (komponent pojawia się na ekranie po raz pierwszy), aktualizację (renderuje się ponownie, bo zmienił się jego stan, propsy albo kontekst) i odmontowanie (zostaje usunięty). W komponentach funkcyjnych na te fazy reagujesz przez useEffect: efekt z [] uruchamia się po zamontowaniu, efekt z zależnościami po aktualizacjach, w których się zmieniły, a funkcja zwracana przez efekt przy odmontowaniu.
Konsola pod podglądem pokazuje każdą fazę w chwili, gdy następuje.
Konsola zaczyna od render, count = 0 i mounted. Każde kliknięcie Count wypisuje renderowanie i aktualizację. Unmount wypisuje unmounted; Mount again zaczyna od nowa od count = 0, bo stan został wyrzucony razem z komponentem.
Montowanie
Komponent montuje się, gdy React pierwszy raz renderuje go w danym miejscu drzewa. React wywołuje funkcję, tworzy węzły DOM z zwróconego JSX, wstawia je, pozwala przeglądarce odmalować ekran, a potem uruchamia efekty komponentu.
useEffect(fn, []) to miejsce, w którym uruchamiasz rzeczy, które mają istnieć tak długo, jak komponent jest na ekranie: subskrypcję, timer, połączenie, pierwsze żądanie danych. Uruchamia się po odmalowaniu, więc nigdy nie opóźnia pierwszej klatki.
Aktualizacja
Komponent się aktualizuje (renderuje ponownie), gdy:
- zmienia się jego własny stan,
- jego rodzic renderuje się ponownie (domyślnie każde dziecko też się renderuje, bez względu na to, czy jego propsy się zmieniły),
- zmienia się kontekst, który czyta.
Po każdej aktualizacji React uruchamia efekty, których zależności się zmieniły. Zanim ponownie uruchomi efekt, uruchamia jego poprzednią funkcję czyszczącą, więc stara subskrypcja zostaje zamknięta, zanim otworzy się nowa. Strona o renderowaniu dokładniej omawia, co wywołuje renderowanie.
Odmontowanie
Komponent się odmontowuje, gdy rodzic przestaje go renderować (jak robi to {show && <Counter />} powyżej), gdy zmienia się jego key albo gdy odmontowany zostaje sam root. React usuwa jego węzły DOM, uruchamia funkcję czyszczącą każdego efektu i wyrzuca jego stan. Jeśli komponent później znowu się pojawi, jest zupełnie nową instancją.
Rodzice i dzieci
React renderuje od góry w dół, ale efekty uruchamia od dołu w górę. Gdy rodzic i dziecko montują się razem, najpierw uruchamiają się efekty dziecka, więc efekt rodzica może polegać na tym, że jego dzieci są w pełni gotowe.
Montowanie wypisuje render Parent, render Child, Child effect, Parent effect. Naciśnij Remove, a funkcje czyszczące uruchomią się w odwrotnej kolejności: Parent cleanup, potem Child cleanup.
Fazy renderowania i zatwierdzania
Każda aktualizacja przechodzi przez dwie fazy:
- Faza renderowania. React wywołuje twoje komponenty, żeby wyliczyć nowy JSX, i porównuje go z poprzednim wynikiem. Na ekranie nic się jeszcze nie zmienia. React może tę pracę wstrzymać, wykonać dwa razy w StrictMode albo wyrzucić, dlatego renderowanie musi być czyste: żadnych żądań, subskrypcji ani zmian zmiennych zewnętrznych.
- Faza zatwierdzania (commit). React stosuje różnice w DOM, podłącza refy i uruchamia callbacki
useLayoutEffect, wszystko zanim przeglądarka odmaluje ekran. CallbackiuseEffecturuchamiają się po tym, zwykle gdy przeglądarka już odmalowała ekran. Gdy aktualizację spowodowało kliknięcie albo inne dyskretne zdarzenie wejściowe, React uruchamia je zamiast tego zaraz po efektach układu, żeby wynik był gotowy przed następnym zdarzeniem.
Każde kliknięcie wypisuje trzy linie w tej samej kolejności. Te efekty nie mają tablicy zależności, więc uruchamiają się po każdym renderowaniu. Dodaj [] jako drugi argument do obu, a uruchomią się tylko po pierwszym.
Metody cyklu życia klas i ich odpowiedniki w hookach
Komponenty klasowe rozpisują cykl życia na nazwane metody. Spotkasz je w starszym kodzie i w granicach błędów, które nadal wymagają klasy.
| Metoda klasy | Kiedy się uruchamia | Odpowiednik w komponencie funkcyjnym |
|---|---|---|
constructor | raz, przed pierwszym renderowaniem | useState(initial), useRef |
static getDerivedStateFromProps | przed każdym renderowaniem | wylicz wartość podczas renderowania albo zresetuj przez key |
render | przy każdym renderowaniu | ciało funkcji |
componentDidMount | po tym, jak pierwsze renderowanie jest w DOM | useEffect(fn, []) |
shouldComponentUpdate | przed aktualizacją, może ją pominąć | React.memo (zwraca odwrotną odpowiedź: „czy propsy są równe") |
getSnapshotBeforeUpdate | tuż przed zmianą DOM | brak bezpośredniego hooka; najbliżej jest useLayoutEffect z refem |
componentDidUpdate | po każdej aktualizacji | useEffect(fn, [deps]) (uruchamia się też po zamontowaniu) |
componentWillUnmount | przed usunięciem | funkcja czyszcząca zwracana przez useEffect |
getDerivedStateFromError, componentDidCatch | gdy dziecko rzuci błąd | brak: napisz klasę granicy błędów |
Oto klasa, która wypisuje każdą metodę w chwili jej wykonania:
Montowanie wypisuje constructor, getDerivedStateFromProps, render, componentDidMount. Kliknięcie wypisuje getDerivedStateFromProps, shouldComponentUpdate, render, getSnapshotBeforeUpdate, componentDidUpdate. Niech shouldComponentUpdate zwraca false, a kliknięcia przestaną aktualizować przycisk: stan się zmienia, ale React pomija renderowanie. Strona o komponentach klasowych pokazuje, jak przerobić taką klasę na funkcję.
Myśl o synchronizacji, a nie o cyklu życia
Tabela hooków przydaje się do czytania starego kodu, ale może skłonić cię do pisania efektów, które kopiują nawyki z klas, na przykład jeden duży efekt „po zamontowaniu" i jeden duży efekt „po aktualizacji". Efekt lepiej czytać jako „utrzymuj tę zewnętrzną rzecz w zgodzie z tymi wartościami": połącz się z roomId, a gdy roomId się zmieni, rozłącz się i połącz ponownie. Pisz jeden efekt na każdą rzecz, którą synchronizujesz, wymień wartości, których używa, jako zależności i pozwól Reactowi zdecydować, kiedy go uruchomić.
Najczęściej zadawane pytania
Jakie są fazy cyklu życia komponentu Reacta?
Montowanie (React tworzy komponent i umieszcza jego wynik na ekranie), aktualizacja (komponent renderuje się ponownie, bo zmienił się jego stan, propsy albo kontekst) i odmontowanie (komponent zostaje usunięty i sprząta po sobie).
Jaki jest odpowiednik componentDidMount w hookach?
useEffect(() => { ... }, []). Pusta tablica zależności sprawia, że efekt uruchamia się raz, po tym, jak pierwsze renderowanie jest na ekranie. Funkcja, którą zwraca, to odpowiednik componentWillUnmount.
Jaki jest odpowiednik componentDidUpdate w hookach?
useEffect z interesującymi cię wartościami w tablicy zależności, na przykład [userId]. W odróżnieniu od componentDidUpdate uruchamia się też po pierwszym renderowaniu, więc obejmuje montowanie i aktualizację razem.
W jakiej kolejności uruchamiają się efekty rodzica i dziecka?
React renderuje od rodzica w dół, ale efekty uruchamia od dzieci w górę: efekt dziecka uruchamia się przed efektem rodzica. Przy odmontowaniu funkcja czyszcząca rodzica uruchamia się przed funkcją czyszczącą dziecka.
Co to są fazy render i commit?
W fazie renderowania React wywołuje twoje komponenty, żeby ustalić, jak ma wyglądać ekran; może tę pracę wstrzymać albo wyrzucić. W fazie zatwierdzania (commit) stosuje zmiany w DOM i uruchamia efekty układu; zwykłe efekty uruchamiają się po tym, zwykle gdy przeglądarka już odmalowała ekran.