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ć.