Dogłębnie poznajemy hook useEffect w Reakcie, niezwykle przydatny i kluczowy hook, który trzeba dobrze zrozumieć, zanim zaczniemy się bawić tą biblioteczką.

Ok, najpierw powiedzmy, czym są reaktywne wartości:

  • propsy
  • stan (pierwsza rzecz zwracana przez useState)
  • wszystkie zmienne, stałe i funkcje zdefiniowane wewnątrz komponentu (top-level)

Teraz powiedzmy sobie, co nie jest reaktywną wartością:

  • stałe, zmienne i funkcje zdefiniowane powyżej poziomu komponentu
  • setState z przekazaną updater function zamiast używający state + coś
  • setState(42) też raczej nie jest, według mnie, nie korzysta z poprzedniego stanu, ale w razie w: setState( (_) => 42) na pewno reaktywny nie będzie…

Ok, to teraz musimy zrozumieć, że wszelkie reaktywne wartości, jakich użyjemy w useEffect będą musiały znaleźć się w tablicy zależności, a my nie wybieramy sobie, jakich zależności chcemy dopisać.

I teraz tak, co przyjmuje useEffect:

  • funkcję callback
    • funkcja callback może mieć zdefiniowane stałe, zmienne i funkcje, i one nie muszą być do zależności dopisywane
    • funkcja callback może zwracać (jako return) funkcję cleanup, której zadaniem jest posprzątanie między re-renderami oraz na unmount
    • sam useEffect niczego nie zwraca
  • tablicę zależności – nie wybieramy, co do niej wpisujemy, tylko podajemy reaktywne wartości, których używamy, ale jest pewien haczyk.

Hook useEffect bez tablicy zależności jest odpalany przy renderze początkowym i każdym re-renderze:

useEffect(() => {
  // ...
}); // Always runs again

Z drugiej strony warto pamiętać, że zmienne, stałe i funkcje utworzone w useEffect nie są tworzone od zera co każdy re-render, a te na top-level komponentu są i są reaktywnymi wartościami (te powyżej poziomu komponentu też nie są).

Hook useEffect z pustą tablicą zależności jest odpalany tylko przy initial render:

useEffect(() => {
  // ...
}, []); // Does not run again (except once in development)

Możemy tam sobie zrobić alercik, że komponent dodany i ruszy tylko jeden raz (w development mode dwa razy, ale możemy go wywalić), potem przy re-renderach już nie będzie ruszać.

Hook useEffect z tablicą zależności jest odpalany przy initial render oraz przy każdym re-renderze, ale tylko wtedy, gdy te zależności uległy zmianie:

useEffect(() => {
  // ...
}, [a, b]); // Runs again if a or b are different

Możemy tam dać conosle loga a i b, ilekroć a lub b się zmieni, on nam to wypisze. Na początku też wypisze (initial render), ale przy innych re-renderach chodzić nie będzie.

Ok, teraz cleanup function:

  • cleanup function chodzi gdy mamy unmount
  • cleanup function chodzi przed każdym re-renderem nie licząc re-renderów, które nie dotyczą zależności
  • cleanup function jest opcjonalne, ale ma mirrorować to, co robi setup (czyli callback do momentu returna)

Ok, przykład dobrego cleanupu z docsów Reactowych:

useEffect(() => {
    const connection = createConnection(serverUrl, roomId);
    connection.connect();
    return () => {
      connection.disconnect();
    };
  }, [serverUrl, roomId]);

Czyli tak:

  • createConnection nie jest zdefiniowana w useEffect, ale wiemy, że nie jest w komponencie, bo nie ma jej w tablicy zależności
  • serverUrl i roomId to albo propsy, albo stan, albo stałe/zmienne zdefiniowane powyżej useEffect ale w obrębie komponentu
  • tworzymy połączenie
  • wywołujemy połącz
  • cleanup function czyści połączenie, rozłącza

Ok, część dalsza w drodze, będziemy kawałek po kawałku to poznawać.