Przejdź do głównej zawartości

03. useEffect i cykl życia

useEffect i cykl życia

useEffect to hook do synchronizacji komponentu ze światem zewnętrznym — pobieranie danych, subskrypcje, manipulacje DOM, timery. Kluczem jest zrozumienie dependency array (kiedy efekt się uruchamia) i funkcji cleanup (co robić przy odmontowaniu komponentu).

  1. Co to jest efekt uboczny? — Każde działanie poza renderowaniem: fetch, timer, subskrypcja, localStorage
  2. Kiedy useEffect się uruchamia? — Zależy od dependency array: [] = raz, [dep] = przy zmianie dep, bez tablicy = każdy render
  3. Co to jest cleanup? — Funkcja zwracana z useEffect, uruchamiana przed każdym ponownym efektem i odmontowaniem
  4. Jak zastąpił klasy?componentDidMount = [], componentDidUpdate = [dep], componentWillUnmount = cleanup
  1. Definicja efektu ubocznego — co to jest, dlaczego jest poza render
  2. Trzy warianty dependency array — bez tablicy, [], [wartości]
  3. Funkcja cleanup — memory leaks, anulowanie fetch, clearInterval
  4. Pobieranie danych — klasyczny fetch w useEffect
  5. Cykl życia klasowy vs hooks — tabela porównawcza

Schemat

Oś czasu komponentu: Mount → Render → useEffect → [zmiana stanu → Render → cleanup → useEffect] → Unmount → cleanup*. Pokaż kiedy każda faza się dzieje.

Przykład kodu JSX

Komponent pobierający dane użytkownika z API — z loading state, error state i cleanup (AbortController do anulowania fetch).

Zawartość:

  • Co to jest useEffect i po co istnieje
  • Trzy warianty dependency array z przykładami
  • Prosty fetch danych w useEffect
  • Schemat: kiedy useEffect się uruchamia

Forma: 10 slajdów, 10 minut prezentacji

Ocena: 3.0
// Uruchamia się przy KAŻDYM renderze (rzadko potrzebne)
useEffect(() => {
console.log('Każdy render');
});
// Uruchamia się TYLKO RAZ po mount
useEffect(() => {
console.log('Mount — tylko raz');
}, []);
// Uruchamia się gdy zmieni się userId
useEffect(() => {
console.log('userId się zmieniło:', userId);
}, [userId]);
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const controller = new AbortController();
async function fetchUser() {
try {
setLoading(true);
const response = await fetch(`/api/users/${userId}`, {
signal: controller.signal,
});
const data = await response.json();
setUser(data);
} catch (err) {
if (err.name !== 'AbortError') {
setError(err.message);
}
} finally {
setLoading(false);
}
}
fetchUser();
// Cleanup: anuluj fetch jeśli userId się zmieni lub komponent odmontuje
return () => controller.abort();
}, [userId]);
if (loading) return <p>Ładowanie...</p>;
if (error) return <p>Błąd: {error}</p>;
return <p>Użytkownik: {user?.name}</p>;
}
// ŹLE: setState na odmontowanym komponencie
useEffect(() => {
const interval = setInterval(() => {
setCount(c => c + 1); // Błąd jeśli komponent jest odmontowany!
}, 1000);
// Brak cleanup — timer działa w tle bez końca
}, []);
// DOBRZE: cleanup czyści timer
useEffect(() => {
const interval = setInterval(() => {
setCount(c => c + 1);
}, 1000);
return () => clearInterval(interval); // Zatrzymaj timer przy odmontowaniu
}, []);

Analogia do cleanup

Cleanup to jak sprzątanie po sobie — jeśli otwierasz połączenie sieciowe, musisz je zamknąć. Jeśli stawiasz timer, musisz go wyłączyć. Bez sprzątania — memory leak.

Pokaż dependency array wizualnie

Narysujcie trzy slajdy: jeden dla każdego wariantu tablicy zależności z animacją osi czasu pokazującą KIEDY efekt odpala.

Typowy błąd do zademonstrowania

Pokażcie warning ESLint react-hooks/exhaustive-deps w edytorze kodu — to konkrety z codziennej pracy.

Q&A — trudne pytanie

“Dlaczego React uruchamia useEffect dwa razy w Strict Mode?” — To celowe! React sprawdza czy cleanup działa poprawnie.

useEffect to klucz do świata zewnętrznego!

Bez useEffect React byłby odizolowaną wyspą bez dostępu do API, timerów i przeglądarki. Ten temat jest używany w każdym projekcie — opanujcie go a będziecie nieocenieni w projekcie semestralnym!