Sprawdź, do czego może Ci sie przydać hook useLayoutEffect!

Spis treści

  1. Wprowadzenie
  2. Przechodzenie dalej bez kliknięcia
    1. Opis przypadku
    2. Implementacja useEffect vs useLayoutEffect
    3. Kod przykładu na Github
    4. Klikalny przykład na Github Pages
  3. Podsumowanie

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:

Końcowy rezultat uruchomienia kodu z hook useLayoutEffect i useEffect

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 komputera za pomocą opcji CPU w zakładce Performance

Spowolnienie to spowoduje pojawienie się różnicy:

  1. Wersja useLayoutEffect zachowuje się po staremu, krok 3 jest pomijany
  2. 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.