Spis treści
Wprowadzenie
UseEffect
to jeden z najczęściej używanych hooków, za pomocą których uzyskujemy efekty podobne jak kiedyś za pomocą metod componentDidMount
, componentDidUpdate
i componentWillMount
. Hook ten jednak wywołuje się po przerysowaniu komponentu. Rzeczy dziejące się w hooku są asynchroniczne w stosunku do rysowania elementów na ekranie i go nie blokują.
Istnieje jednak również hook useLayoutEffect – wykonywany przed przerysowaniem elementu, blokujące aktualizację tego co widzi użytkownik. Jak się okazuje, są przypadki gdy takie zachowanie jest nam potrzebne – mimo że w większości przypadków wystarczy nam useEffect
.
Zapraszam Cię więc do artykułu, w którym przedstawię taki przypadek – luźno bazujący na tym, co niedawno przydarzyło mi się w pracy.
Przechodzenie dalej bez kliknięcia
Opis przypadku
Łatwo wyobrazić sobie sytuację, gdy mamy np duży formularz składający się z kilku kroków i część danych może być dostępna zanim użytkownik je wypełni – np jest to formularz zamówienia jedzenia i przy poprzednich zamówieniach podał on już adres który został zapisany w localStorage.
Możemy więc chcieć pominąć krok jeżeli już mamy do niego dane.
Implementacja useEffect vs useLayoutEffect
Załóżmy, że mamy listę kroków od 1 do X i chcemy pominąć krok 3. Komponent Step
to informacja na którym kroku jesteśmy oraz możliwość przejścia dalej:
const Step = ({ step, onClick }) => { return ( <div> You are on step {step}. <button onClick={onClick}>Next</button> </div> ) }
Przygotujmy 2 implementacje pomijania kroku – pierwszą z useEffect
i drugą z useLayoutEffect
, wyglądające niemal bliźniaczo:
const StepUseEffect = ({ step, onClick }) => { useEffect(() => { if (step === 3) { onClick(); } }, [step, onClick]); return <Step onClick={onClick} step={step} /> } const StepUseLayoutEffect = ({ step, onClick }) => { useLayoutEffect(() => { if (step === 3) { onClick(); } }, [step, onClick]); return <Step onClick={onClick} step={step} /> }
Czyli zamiast czekać aż użytkownik kliknie, na kroku który chcemy pominąć “klikamy” automatycznie za niego. Rezultat uruchomienia wygląda tak:
Przy zwyczajnym klikaniu różnica jest niezauważalna – oba przykłady po prostu pomijają krok 3.
Co jednak się stanie gdy spowolnimy działanie aplikacji? W zakładce Performance w DevTools możemy zasymulować słabszy komputer zmieniając ustawienia opóźnienia CPU:
Spowolnienie to spowoduje pojawienie się różnicy:
- Wersja
useLayoutEffect
zachowuje się po staremu, krok 3 jest pomijany - Wersja z
useEffect
pokazuje na chwilę krok 3 po potem przeskoczyć do kroku 4. To dlatego, że akcja pominięcia kroku wykonuje się po przerysowaniu – krok 3 jest wyświetlony użytkownikowi na ekranie a dopiero potem pojawia się akcja która powoduje pominięcie.
Kod przykładu na Github
Pełny kod przykładu znajdziesz pod adresem:
https://github.com/radek-anuszewski/use-layout-hook-demo
Klikalny przykład na Github Pages
Przykład który możesz poklikać znajduje się pod adresem:
https://radek-anuszewski.github.io/use-layout-hook-demo/
Podsumowanie
Jak się okazuje, nawet jak pozornie dziwna konstrukcja jak hook useLayoutEffect
znajduje swoje zastosowanie w praktyce – może być ratunkiem w sytuacji, gdy nie ma czasu bądź możliwości na kompleksowe zmiany by pozbyć się problemu w lepszy i ładniejszy sposób. Pierwszym wyborem podczas codziennej pracy powinien być jednak useEffect
, który nie blokuje rysowania UI.