Haki w React stanowią potężne narzędzie do zarządzania efektami ubocznymi w obrębie komponentów funkcyjnych. Wśród nich, trzy wyróżniają się popularnością przy obsłudze efektów: useEffect
, useLayoutEffect
oraz useEffectEvent
. Każdy z nich charakteryzuje się unikalnym zastosowaniem, dlatego kluczowe jest odpowiednie dobranie haka do konkretnego zadania.
useEffect
– Podstawowy Hak Efektów
useEffect
jest fundamentalnym hakiem React, pozwalającym na realizację efektów ubocznych, takich jak interakcja z drzewem DOM, wykonywanie operacji asynchronicznych oraz pobieranie danych w komponentach funkcyjnych. Działa on jako funkcja przyjmująca dwa argumenty: funkcję efektu oraz tablicę zależności.
Funkcja efektu zawiera kod, który realizuje dany efekt uboczny, natomiast tablica zależności określa moment uruchomienia tej funkcji. W przypadku, gdy tablica zależności jest pusta, funkcja efektu zostanie wykonana jednokrotnie – podczas inicjalnego renderowania komponentu. W przeciwnym przypadku, funkcja efektu zostanie uruchomiona za każdym razem, gdy zmieni się jakakolwiek wartość z tablicy zależności.
Poniżej przykład wykorzystania useEffect
do pobierania danych:
import React from "react"; function App() { const [data, setData] = React.useState([]); React.useEffect(() => { fetch("https://jsonplaceholder.typicode.com/posts") .then((response) => response.json()) .then((data) => setData(data)); }, []); return ( <div className="app"> {data.map((item) => ( <div key={item.id}>{item.title}</div> ))} </div> ); } export default App;
Ten fragment kodu przedstawia komponent aplikacji, który korzystając z useEffect
, pobiera dane z zewnętrznego API. Funkcja efektu pobiera przykładowe dane z API JSONPlaceholder, następnie przetwarza odpowiedź JSON i aktualizuje stan danych.
Na podstawie stanu danych, komponent aplikacji wyświetla tytuł każdego z elementów pobranych danych.
Główne cechy haka useEffect
- Obsługa asynchroniczności: Zapewnia natywną obsługę operacji asynchronicznych, co ułatwia pobieranie danych.
- Działanie po renderowaniu: Efekty
useEffect
są realizowane dopiero po wyrenderowaniu komponentu, co zapobiega blokowaniu interfejsu użytkownika. - Mechanizm czyszczenia: Oferuje wbudowany sposób na „posprzątanie” po wykonaniu efektu, poprzez zwrócenie funkcji, co jest szczególnie przydatne podczas pracy z nasłuchiwaniem zdarzeń czy subskrypcjami.
useLayoutEffect
– Synchronizacja z DOM
Hak useLayoutEffect
jest zbliżony do useEffect
, lecz działa synchronicznie – po wszystkich zmianach w DOM. Oznacza to, że wykonywany jest on zanim przeglądarka odrysuje ekran, co czyni go idealnym do zadań wymagających precyzyjnej kontroli nad układem i stylami DOM, takich jak pomiar rozmiarów elementów, ich modyfikacja czy animacje.
Przykładowe zastosowanie useLayoutEffect
do zmiany szerokości elementu button:
import React from "react"; function App() { const button = React.useRef(); React.useLayoutEffect(() => { const { width } = button.current.getBoundingClientRect(); button.current.style.width = `${width + 12}px`; }, []); return ( <div className="app"> <button ref={button}>Click Me</button> </div> ); } export default App;
Powyższy kod zwiększa szerokość przycisku o 12 pikseli. Dzieje się to za pomocą useLayoutEffect
, dzięki czemu modyfikacja szerokości przycisku następuje przed jego wyświetleniem na ekranie.
Charakterystyka useLayoutEffect
- Synchroniczność: Hak
useLayoutEffect
wykonuje swoje działania synchronicznie, co potencjalnie może powodować blokowanie interfejsu, jeśli operacje w nim są złożone. - Bezpośrednia interakcja z DOM: Najlepiej sprawdza się w sytuacjach, gdzie wymagany jest bezpośredni odczyt i zapis danych w DOM, zwłaszcza jeśli zmiany mają zajść przed ponownym odświeżeniem przeglądarki.
useEffectEvent
– Hak do Obsługi Zdarzeń
useEffectEvent
to hak React, który rozwiązuje problem zależności występujący przy useEffect
. Osoby zaznajomione z useEffect
wiedzą, że tablica zależności może być źródłem problemów, czasami wymagając dodania do niej wartości, które nie są w pełni konieczne.
Przykład:
import React from "react"; function App() { const connect = (url) => { }; const logConnection = (message, loginOptions) => { }; const onConnected = (url, loginOptions) => { logConnection(`Connected to ${url}`, loginOptions); }; React.useEffect(() => { const device = connect(url); device.onConnected(() => { onConnected(url); }); return () => { device.disconnect(); }; }, [url, onConnected]); return <div></div>; } export default App;
Ten kod pokazuje komponent aplikacji, który zarządza połączeniem z zewnętrzną usługą. Funkcja connect
służy do połączenia z podanym URL-em, logConnection
rejestruje informacje o połączeniu, a onConnected
wywołuje logConnection
po pomyślnym połączeniu.
Hak useEffect
wywołuje funkcję connect
, a następnie ustawia funkcję onConnected
, która zostanie wykonana po wywołaniu zdarzenia onConnected
przez urządzenie. Funkcja ta rejestruje komunikat o połączeniu. Zwraca ona również funkcję czyszczącą, która jest wykonywana przy odmontowaniu komponentu i odpowiada za rozłączenie z urządzeniem.
Tablica zależności zawiera zmienną url
i funkcję onConnected
. Komponent App będzie tworzyć nową funkcję onConnected
przy każdym renderowaniu. To spowoduje, że useEffect
zacznie działać w pętli, co z kolei będzie powodowało ciągłe ponowne renderowanie komponentu aplikacji.
Istnieje wiele sposobów na poradzenie sobie z tą pętlą useEffect
. Jednak najefektywniejszym sposobem, który pozwala uniknąć dodawania kolejnych niepotrzebnych wartości do tablicy zależności, jest użycie haka useEffectEvent
.
import React from "react"; function App() { const connect = (url) => { }; const logConnection = (message, loginOptions) => { }; const onConnected = React.useEffectEvent((url, loginOptions) => { logConnection(`Connected to ${url}`, loginOptions); }); React.useEffect(() => { const device = connect(url); device.onConnected(() => { onConnected(url); }); return () => { device.disconnect(); }; }, [url]); return <div></div>; } export default App;
Poprzez „opakowanie” funkcji onConnected
w useEffectEvent
, hak ten może zawsze odczytać najświeższe wartości parametrów message
oraz loginOptions
przed przekazaniem ich do useEffect
. Oznacza to, że useEffect
nie musi zależeć od funkcji onConnected
i przekazywanych do niej wartości.
useEffectEvent
jest szczególnie przydatny, gdy efekt useEffect
ma zależeć od konkretnej wartości, nawet jeśli efekt ten wyzwala zdarzenie, które wymaga innych wartości, których nie chcemy uwzględniać jako zależności w useEffect
.
Główne cechy useEffectEvent
- Idealny do efektów ubocznych zależnych od zdarzeń.
- Nie działa z handlerami zdarzeń (takimi jak
onClick
,onChange
, itd.).
Warto zaznaczyć, że useEffectEvent
ma status eksperymentalny i nie jest dostępny w stabilnej wersji React 18.
Którego haka kiedy użyć?
Każdy z opisanych haków do obsługi efektów ubocznych sprawdza się w różnych scenariuszach:
- Pobieranie danych:
useEffect
to idealny wybór. - Bezpośrednia manipulacja DOM: Gdy chcemy dokonać synchronicznych zmian w DOM przed odświeżeniem ekranu, wybierz
useLayoutEffect
. - Lekkie operacje: Jeśli operacje, które wykonujemy, nie stwarzają ryzyka zablokowania interfejsu użytkownika, możemy używać
useEffect
. - Efekty uboczne sterowane zdarzeniami: Użyj
useEffectEvent
do „opakowania” zdarzeń iuseEffect
do uruchamiania efektów ubocznych.
Efektywne zarządzanie efektami ubocznymi
Haki w React otwierają przed programistami wiele możliwości. Zrozumienie różnic między useEffect
, useLayoutEffect
oraz useEffectEvent
ma kluczowe znaczenie dla efektywnej obsługi efektów ubocznych oraz manipulacji DOM. Wybierając odpowiednie haki, można tworzyć aplikacje o wysokiej jakości, zapewniając płynne działanie i pozytywne doświadczenia użytkownika.
newsblog.pl