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 — 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.
import { useEffect } from 'react';
function UserProfile({ userId }) {
useEffect(() => {
fetch(`/api/users/${userId}`)
.then(res => res.json())
.then(data => setUser(data));
}, [userId]);
}
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ă.
| Etapa | Acțiunea React | Când se execută |
|---|---|---|
| Montare | Apelarea funcției efect | După prima randare |
| Actualizare | cleanup → efect | La schimbarea dependențelor |
| Demontare | Doar cleanup | La îndepărtarea componentei |
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.
// 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ă.
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țe | Comportament | Analog din clase |
|---|---|---|
| Fără argument | După fiecare randare | componentDidUpdate |
| [] | O singură dată la montare | componentDidMount |
| [a, b] | La schimbarea lui a sau b | Analog componentWillReceiveProps |
| return cleanup | Gestionarea demontării | componentWillUnmount |
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.
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.
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.
// ❌ 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
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(); }, []).
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.
Î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ă.
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».
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
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.
Citiți și