Aplikacje React są budowane z komponentów, a każdy komponent prowadzi własne życie. Pojawia się na ekranie. Reaguje na nowe informacje. W końcu znika. Zignoruj ten rytm, a zapłacisz wysoką cenę. Pojawiają się wycieki pamięci. Wydajność renderowania spada. Użytkownicy klikają przyciski, które już nie reagują, ponieważ komponent przypisany do nich został odmontowany dziesięć minut temu, ale listener zdarzeń wciąż działa. Zrozumienie cyklu życia komponentu to nie ćwiczenie akademickie. To fundament pisania oprogramowania, które pozostaje niezawodne w rzeczywistym użytkowaniu.
Myśl o istnieniu komponentu w trzech etapach. Programiści często opisują je jako narodziny, wzrost i śmierć. React nazywa je montowaniem (mounting), aktualizacją (updating) i odmontowywaniem (unmounting). Każdy etap ma konkretne zadanie, a każdy z nich daje precyzyjny moment na wykonanie kodu lub jego sprzątnięcie.
Trzy fazy
Montowanie (Mounting) to narodziny. Komponent zostaje utworzony i po raz pierwszy wstawiony do DOM przeglądarki. To okno konfiguracyjne. Jeśli komponent potrzebuje danych zdalnych, to jest moment, w którym je pobierasz. Jeśli potrzebuje stanu początkowego obliczonego na podstawie propsów, robisz to właśnie tutaj. W erze komponentów klasowych należało to do domeny componentDidMount. W nowoczesnych komponentach funkcyjnych obsługujesz to za pomocą useEffect i pustej tablicy zależności. Kluczowym szczegółem jest to, że ten blok kodu wykonuje się tylko raz, natychmiast po tym, jak React umieści element na stronie. Dzięki temu jest idealny do jednorazowej konfiguracji, takiej jak pobieranie profilu użytkownika czy odczytywanie wartości z localStorage.
Aktualizacja (Updating) to wzrost. Zamontowany komponent nie pozostaje w bezruchu. Przychodzą nowe propsy od rodzica. Zmienia się stan w wyniku interakcji użytkownika. Sam rodzic renderuje się ponownie, wymuszając to samo na dziecku. React renderuje następnie komponent ponownie, aby utrzymać interfejs użytkownika w synchronizacji z danymi. Ta faza powtarza się wielokrotnie w cyklu życia komponentu. Za każdym razem, gdy zmienia się zależność, Twoje efekty (effects) uruchamiają się ponownie, a komponent wprowadza nową zawartość do DOM. Ponieważ ta faza wyzwalana jest tak często, to właśnie tutaj najczęściej zaczynają się problemy z wydajnością. Niepotrzebna praca na tym etapie szybko się kumuluje.
Odmontowywanie (Unmounting) to śmierć. Komponent znika z ekranu, być może dlatego, że użytkownik przeszedł na inną stronę lub renderowanie warunkowe zostało wyłączone. W tym momencie komponent zostaje zniszczony, ale świat zewnętrzny nie zawsze o tym wie. Timery nadal tykają. WebSockety nadal nasłuchują. Listenery zdarzeń w obiekcie window pozostają podpięte. Jeśli ich nie posprzątasz, będą trzymać referencje do martwych komponentów, co spowoduje wycieki pamięci. Przeglądarka marnuje cykle procesora na „duchy”. React udostępnia funkcję sprzątającą (cleanup function) właśnie po to, abyś mógł zatrzymać interwały, zamknąć połączenia i usunąć listenery, zanim komponent zniknie na dobre.
Od metod klasowych do Hooków
Przez lata inżynierowie React zarządzali tymi fazami za pomocą komponentów klasowych. Logika była rozproszona pomiędzy odrębnymi metodami. Pobierałeś dane wewnątrz componentDidMount, odpowiadałeś na zmiany propsów w componentDidUpdate i „zamiatałeś podłogę” w componentWillUnmount. Problem polegał na tym, że powiązana ze sobą logika okazywała się rozproszona. Pojedyncza funkcja, taka jak połączenie z czatem na żywo, wymagała kodu w trzech oddzielnych metodach cyklu życia tylko po to, aby się połączyć, sprawdzać zmiany wątków i rozłączyć się.
Hooki to zmieniły. Hook useEffect w komponentach funkcyjnych pozwala grupować logikę według przeznaczenia, a nie według fazy cyklu życia. Mimo to, model mentalny pozostaje przejrzysty, jeśli wiesz, gdzie szukać.
Aby obsłużyć montowanie, napisz useEffect(() => { ... }, []). Pusta tablica zależności mówi Reactowi, że ten efekt nie ma zewnętrznych zależności, więc uruchomi się tylko po początkowym renderowaniu. Zastępuje to componentDidMount.
Aby obsłużyć aktualizacje, uwzględnij konkretne wartości, które React powinien śledzić: useEffect(() => { ... }, [dependency]). Gdy dependency zmieni się między renderami,
