React: Do czego służy useEffect?

Opublikowano: 17.01.2023 - tagi: JavaScript React Hook Komponent Stan

Kiedy użyć useEffect?

useEffect jest to hook wprowadzony w React w wersji 16.8 dla komponentów stworzonych przez funkcję. Jego zadaniem jest obsługa różnego rodzaju efektów ubocznych. A jaśniej?

Za efekty uboczne można uznać: komunikacja za pomocą requestów, tworzenie subskrypcji (na przykład: obsługa RxJS), ale także ich kasowanie. Do tej kategorii zalicza się także obsługa timerów: setInterval oraz setTimeout.

useEffect ma wiele wspólnego z cyklem istnienia komponentu. Chcesz wywołać jakiś kod, gdy komponent został stworzony? Użyj useEffect. Chcesz posprzątać po sobie, gdy komponent jest usuwany? Użyj useEffect. Potrzebujesz wywołać dany fragment kodu, gdy została zmieniona wartość jakiejś zmiennej? Użyj useEffect.

Poniżej przykład, z czego się składa useEffect:

import React, { useEffect } from 'react';

useEffect(() => {

}, []);

Pierwszy parametr to funkcja, która zostanie wywołana w określonym przez Ciebie momencie: tutaj po prostu umieszczamy nasz kod. Drugi parametr jest opcjonalny, jest to tablica zależności. Przekazujemy tam zmienne, na które chcesz zareagować, gdy zmieni się ich wartość. Tablica zależności może być pusta lub nie musisz jej w ogóle przekazywać, co z kolei ma swoje konsekwencje. O tym piszę w dalszej części wpisu.

Przykład 1: Za każdym razem

Jak wspomniałem we wstępie nie musimy przekazywać tablicy zależności do useEffect:

import React, { useEffect, useState } from 'react';

const App = () => {
    const [count, setCount] = useState(0);

    useEffect(() => {
        console.log(`Count: ${count}`);
    });

    const onClick = () => {
        setCount(count + 1);
    }

    return (
        <button onClick={onClick} type="button">Click me!</button>
    )
}

Po uruchomieniu aplikacji. W konsoli zostanie wyświetlone Counter: 0. To oznacza, że useEffect wywołał kod, gdy komponent został stworzony.

Po kliknięciu przycisk mamy w konsoli: Counter: 1. Możemy z tego wyciągnąć wniosek, że useEffect wywołuje nasz kod, w momencie, gdy stan komponentu się zmienił.

Wnioski: jeśli nie przekażesz tablicy zależności do useEffect kod tam umieszczony zostanie: a) Wywołany w momencie, gdy komponent zostanie stworzony b) Wywołany, gdy stan komponentu się zmieni

Przykład 2: Komponent został stworzony

Jeśli chcemy wywołać kod tylko w momencie, gdy komponent zostanie stworzony, wystarczy przekazać pustą tablicę jako drugi parametr:

import React, { useEffect, useState } from 'react';

const App = () => {
    const [count, setCount] = useState(0);

    useEffect(() => {
        console.log('Component is mounted!')
    }, []);

    const onClick = () => {
        setCount(count + 1);
    }

    return (
        <button onClick={onClick} type="button">Click me!</button>
    )
}

Nawet jeśli zmienisz stan komponentu, wciskając przycisk: kod umieszczony w useEffect nie zostanie wywołany.

Przykład 3: Zmiana wartości zmiennej

Załóżmy, że chcesz zareagować w momencie zmiany stanu danej zmiennej:

import React, { useEffect, useState } from 'react';

const App = () => {
    const [count, setCount] = useState(0);

    useEffect(() => {
        console.log(`Count: ${count}`);
    }, [count]);

    const onIcrement = () => {
        setCount(count + 1);
    }

    const onDecrement = () => {
        setCount(count - 1);
    }

    return (
        <button onClick={onIcrement} type="button">Increment</button>
        <button onClick={onDecrement} type="button">Decrement</button>
    )
}

W momencie stworzenia komponentu w konsoli zostanie wyświetlony log: Count: 0. Jest tak, ponieważ zmiennej count ustawiamy początkowy stan za pomocą useState.

Klikając w oba przyciski będziemy zmieniać stan zmiennej count więc kod umieszczony w useEffect za każdym razem zostanie wywołany.

Przykład 4: Sprzątanie po sobie

useEffect pozwala nam także posprzątać po sobie, jeśli będzie taka potrzeba. Wygląda to tak:

useEffect(() => {
		...
		return () => { ... };
}, [count]);

Wystarczy zwrócić funkcję, a w niej zdefiniować kod sprzątający.

Przykład:

const Count = () => {
    const [count, setCount] = useState(0);

    useEffect(() => {
        let intervalId = null;
        intervalId = setInterval(() => {
            console.log('Tik Tok')
            setCount(c => c + 1);
        }, 1000);

        return () => {
            clearInterval(intervalId);
        }
    }, []);

    return <div>Count: {count}</div>
}

const App = () => {
    const [clicked, setClicked] = useState(true);

    return <div>
            {clicked && <Count></Count>}
            <button onClick={() => setClicked(!clicked)}>Click me!</button>
        </div>;
}

Mamy tutaj komponent Count. W momencie stworzenia tego komponentu uruchamiany jest kod umieszczony w useEffect. Co sekundę zwiększany jest licznik stałej count. Z tego powodu, że używamy funkcji setInterval wypadałoby po sobie posprzątać, w momencie, gdy komponent nie jest używany. Robione jest to za pomocą funkcji clearInterval.

Żeby zademonstrować, że funkcja czyszcząca działa wciśnij przycisk. W konsoli nie będzie wyświetlony log: Tik Tok