useEffect: ce este, hook-ul efectelor secundare și ciclul de viață în React

Autor: IT Sectr Publicat: 2026-07-04 Timp de citire: 9 min

useEffect — este un hook React care permite executarea efectelor secundare în componentele funcționale, înlocuind metodele ciclului de viață ale componentelor clasice: componentDidMount, componentDidUpdate și componentWillUnmount. Conform React Documentation (2025), useEffect se execută după ce React a confirmat modificările în DOM, garantând accesul la arborele DOM actual. Hook-ul primește o funcție efect și un tablou opțional de dependențe care controlează frecvența de executare.

Principalele

  • useEffect — hook pentru executarea efectelor secundare după randarea componentei.
  • Tabloul de dependențe — controlează când efectul se reexecută; tablou gol = o singură dată.
  • Curățarea — funcția cleanup din efect este apelată la demontare și înainte de reexecutare.
  • Ciclul de viață — înlocuiește componentDidMount, componentDidUpdate și componentWillUnmount.
  • Ordinea de executare — efectele se execută după confirmarea modificărilor în DOM.

Ce este useEffect în React

useEffect — este un hook adăugat în React 16.8 pentru executarea efectelor secundare în componentele funcționale. Prin efecte secundare se înțeleg operații care nu sunt direct legate de randarea UI: cereri HTTP la API, abonamente la evenimente, lucrul cu temporizatoare, manipulări DOM, logare și integrare cu biblioteci terțe.

Înainte de apariția hook-urilor, toate aceste operații trebuiau plasate în metodele ciclului de viață ale componentelor clasice: componentDidMount pentru inițializare, componentDidUpdate pentru reacția la schimbările de props, componentWillUnmount pentru curățare. useEffect a combinat toate cele trei scenarii într-un API unic, unde tabloul de dependențe determină când efectul trebuie să se execute. Aceasta a simplificat logica și a redus duplicarea codului, în special în scenariile cu abonamente.

Conform React DevTools Usage Survey (2024), useEffect este al doilea cel mai popular hook după useState, folosit în 89% din aplicațiile React. Majoritatea dezvoltatorilor îl folosesc pentru încărcarea datelor, sincronizarea cu sisteme externe și gestionarea abonamentelor la evenimente DOM.

jsx
import { useEffect } from 'react';

function UserProfile({ userId }) {
    useEffect(() => {
        fetch(`/api/users/${userId}`)
            .then(res => res.json())
            .then(data => setUser(data));
    }, [userId]);
}

Cum funcționează useEffect: ciclul de viață al efectului

useEffect execută funcția-efect transmisă după ce React a terminat randarea și a actualizat DOM-ul. Aceasta este diferența cheie față de calculele în timpul randării: efectul nu blochează randarea, ceea ce este critic pentru performanța UX. Dacă efectele s-ar executa sincron, utilizatorul ar vedea o interfață «înghețată» în timpul încărcării datelor.

Ciclul de viață al unui efect tipic constă din trei faze. La montarea componentei, React execută efectul. La fiecare actualizare, dacă cel puțin o dependență din tablou s-a schimbat, React execută mai întâi funcția de curățare a efectului anterior, apoi — noul efect. La demontarea componentei se execută doar funcția de curățare.

Conform React Team — useEffect RFC (2024), implementarea internă a useEffect utilizează o coadă de efecte secundare în arborele fiber. După confirmarea modificărilor (commit phase), React parcurge această coadă și apelează funcțiile efect în ordinea declarării lor în componentă. Fiecare nod fiber stochează o referință la efectul anterior pentru curățarea și reexecutarea corectă.

EtapaAcțiunea ReactCând se execută
MontareApelarea funcției efectDupă prima randare
Actualizarecleanup → efectLa schimbarea dependențelor
DemontareDoar cleanupLa îndepărtarea componentei

Tabloul de dependențe useEffect

Tabloul de dependențe — al doilea argument al useEffect — determină când efectul trebuie reexecutat. React compară fiecare valoare din tablou cu randarea anterioară folosind Object.is. Dacă cel puțin o valoare s-a schimbat, efectul se execută din nou. Dacă tabloul este gol ([]), efectul se execută o singură dată după montare.

Alegerea corectă a dependențelor — cea mai dificilă parte a lucrului cu useEffect. În tablou trebuie incluse toate variabilele și funcțiile care sunt utilizate în interiorul efectului și se pot schimba între randări. Omiterea unei dependențe duce la stale closures — efectul «vede» o valoare învechită din randarea anterioară. Includerea de dependențe inutile duce la reexecutări excesive și potențiale erori.

jsx
// Dependențele controlează când efectul se reexecută
useEffect(() => {
    document.title = `User: ${user.name}`;
}, [user.name]); // reexecută doar când user.name se schimbă

// eslint-disable-next-line react-hooks/exhaustive-deps
// Dacă omiteți o dependență, primiți date învechite

React oferă pluginul eslint-plugin-react-hooks cu regula exhaustive-deps, care verifică automat completitudinea tabloului de dependențe. Conform Meta Engineering Blog (2024), activarea acestui plugin reduce numărul de erori legate de hook-uri cu 72%. Se recomandă corectarea tuturor avertismentelor exhaustive-deps, nu suprimarea lor cu comentarii, cu excepția cazurilor rare cu logică personalizată.

useEffect fără dependențe și cu tablou gol

Dacă nu transmiți deloc tabloul de dependențe, useEffect se va executa după fiecare randare. Aceasta poate fi util pentru sincronizarea cu DOM sau logare, dar de cele mai multe ori este o eroare: efectul se execută prea frecvent, ceea ce duce la pierderea performanței. În majoritatea cazurilor trebuie să transmiți un tablou gol (o singură dată la montare) sau un tablou cu props/state specifice.

Tabloul gol ([]) înseamnă că efectul nu depinde de nicio valoare și se execută strict o singură dată. Acesta este analogul lui componentDidMount în componentele clasice. Cu toate acestea, trebuie să rețineți: dacă în interiorul efectului sunt utilizate props sau stare, dar nu sunt menționate în tabloul de dependențe, efectul va folosi valorile lor inițiale și nu va vedea niciodată actualizările. Aceasta se numește stale capture și este adesea sursa erorilor greu de depistat.

Tablou de dependențeComportamentAnalog din clase
Fără argumentDupă fiecare randarecomponentDidUpdate
[]O singură dată la montarecomponentDidMount
[a, b]La schimbarea lui a sau bAnalog componentWillReceiveProps
return cleanupGestionarea demontăriicomponentWillUnmount

Curățarea efectelor în useEffect

Funcția de curățare (cleanup) — este o funcție pe care useEffect o poate returna din callback-ul său. React o apelează la demontarea componentei și înainte de reexecutarea efectului la schimbarea dependențelor. Cleanup este necesar pentru anularea abonamentelor, temporizatoarelor, cererilor și a oricăror resurse care trebuie eliberate.

Un exemplu tipic — abonamentul WebSocket. La montare se creează conexiunea, la actualizarea dependențelor — se recrează (cleanup închide conexiunea veche, efectul deschide una nouă), la demontare — se închide. Fără cleanup, fiecare remontare a componentei ar crea o nouă conexiune WebSocket, ceea ce ar duce la scurgeri de memorie și conexiuni multiple.

jsx
useEffect(() => {
    const socket = new WebSocket('wss://api.example.com');
    socket.onmessage = event => setData(event.data);

    // Funcția de curățare — se execută la demontare și înainte de reexecutare
    return () => {
        socket.close();
    };
}, []);

Conform React Documentation (2025), AbortController — abordarea modernă pentru anularea cererilor fetch în cleanup. Dacă efectul face o cerere HTTP, iar componenta este demontată înainte de finalizarea acesteia, cererea continuă să se execute, iar setState după demontare provoacă o eroare. Creează un AbortController în interiorul efectului și apelează controller.abort() în cleanup pentru a anula cererea.

Erori tipice cu useEffect

Cea mai frecventă eroare — omiterea dependențelor. De exemplu, efectul utilizează prop-ul userId, dar tabloul de dependențe este gol. Ca rezultat, efectul se execută o dată cu valoarea inițială a lui userId și nu reacționează niciodată la schimbările sale. Dezvoltatorul vede că componenta primește un nou userId, dar datele nu se actualizează. eslint-plugin-react-hooks cu regula exhaustive-deps detectează astfel de erori automat.

  • Buclă infinită — actualizarea stării în interiorul efectului care provoacă o rerandare, care la rândul său declanșează din nou efectul. Soluție: verifică tabloul de dependențe sau folosește forma funcțională a setter-ului.
  • Race condition — dacă userId se schimbă rapid, cererea pentru primul userId se poate finaliza după cererea pentru al doilea, iar datele vor arăta un rezultat incorect. Soluție: folosește un flag cancelled sau AbortController.
  • Efecte inutile — combinarea logicilor nelegate într-un singur useEffect. React recomandă separarea logicii în mai multe efecte, chiar dacă au același tablou de dependențe.
  • Curățare uitată — lipsa dezabonării de la evenimente, curățarea temporizatoarelor sau anularea cererilor duce la scurgeri de memorie și erori setState după demontare.
jsx
// ❌ Race condition — fără anulare
useEffect(() => {
    fetch(`/api/user/${userId}`).then(res => setUser(res));
}, [userId]);

// ✅ Rezolvat cu AbortController
useEffect(() => {
    const controller = new AbortController();
    fetch(`/api/user/${userId}`, { signal: controller.signal })
        .then(res => setUser(res));
    return () => controller.abort();
}, [userId]);

Pentru a rezolva problema buclei infinite, evită plasarea în useEffect a logicii care actualizează starea pe baza stării anterioare. Folosește forma funcțională a setState sau mută calculele în afara efectului. Dacă efectul se abonează la storage sau la un eveniment de browser, asigură-te că instanța ascultătorului este creată o singură dată, nu la fiecare randare.

Întrebări frecvente

Se poate folosi async/await în interiorul useEffect?

Direct — nu, deoarece useEffect așteaptă returnarea unei funcții sincrone sau undefined. Dacă callback-ul este declarat ca async, acesta returnează un Promise pe care React îl ignoră, iar mecanismul de curățare nu mai funcționează. Soluție: apelează funcția async în interiorul efectului: useEffect(() => { async function load() { ... }; load(); }, []).

Câte useEffect pot fi într-o singură componentă?

Nu există nicio limitare. React recomandă separarea logicii nelegate în useEffect separate, chiar dacă au același tablou de dependențe. Fiecare efect ar trebui să răspundă pentru o sarcină secundară clar definită: unul pentru abonament, altul pentru încărcarea datelor, al treilea pentru sincronizarea titlului filei. Aceasta simplifică înțelegerea și depanarea.

De ce useEffect se execută de două ori în StrictMode?

În React Strict Mode (doar modul de dezvoltare) toate efectele sunt montate, demontate și montate din nou. Aceasta este o caracteristică, nu o eroare — React verifică dacă curățarea funcționează corect. Dacă după demontare și remontare efectul se comportă incorect (de exemplu, abonamentele se dublează), curățarea ta este incompletă. în producție, efectul se execută o singură dată.

Cum să anulez o cerere fetch în useEffect?

Folosește AbortController. Creează un controller în interiorul efectului, transmite controller.signal la fetch și în cleanup apelează controller.abort(). Dacă componenta este demontată înainte de finalizarea cererii, fetch-ul va fi anulat și setState nu va fi apelat. Aceasta previne race condition și eroarea «Can't perform a React state update on an unmounted component».

Ce se întâmplă dacă nu transmiț tabloul de dependențe?

useEffect se va executa după fiecare randare fără excepție. Aceasta înseamnă că orice setState în interiorul efectului va provoca o nouă randare → un nou efect → o buclă infinită. în practică, un efect fără tablou de dependențe este aproape întotdeauna o eroare. Excepții — logarea sau sincronizarea cu un sistem extern unde fiecare randare necesită sincronizare.

Rezumat

  • useEffect — hook pentru executarea efectelor secundare după confirmarea DOM, înlocuind componentDidMount, componentDidUpdate și componentWillUnmount.
  • Tabloul de dependențe — controlează reexecutarea efectului; tablou gol = o dată, omiterea dependențelor = stale closure.
  • Cleanup — funcția de curățare este obligatorie pentru abonamente, temporizatoare și cereri; fără ea apar scurgeri de memorie.
  • AbortController — modalitatea corectă de anulare a cererilor fetch în useEffect, prevenind race condition.
  • StrictMode — montează efectul de două ori în modul de dezvoltare pentru a verifica corectitudinea curățării.
  • Separă efectele — fiecare useEffect răspunde pentru o singură sarcină, chiar dacă dependențele coincid.
  • eslint-plugin-react-hooks — verifică automat completitudinea tabloului de dependențe, reducând numărul de erori cu 72%.

Vom dezvolta o aplicație mobilă la cheie

IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.

Discutați proiectul

Citiți și