Efecte și useRef
Când ai nevoie de useEffect, cleanup și când NU ai nevoie de el.
Actualizat
Ce este un efect
O componentă trebuie să fie pură — doar calculează JSX. Dar uneori trebuie să sincronizezi componenta cu ceva din afara React-ului: un timer, o conexiune WebSocket, un API de browser, o librărie non-React. Pentru asta există useEffect: cod care rulează după ce React a actualizat ecranul.
useEffect(() => {
const id = setInterval(tick, 1000) // pornești sincronizarea
return () => clearInterval(id) // cleanup: o oprești
}, []) // dependențeCiclul de viață al unui efect
- Componenta se randează și apare pe ecran.
- Efectul rulează.
- Când dependențele se schimbă: React rulează cleanup-ul vechi, apoi efectul din nou.
- La demontare: rulează cleanup-ul.
Dependențele
| Al doilea argument | Efectul rulează |
|---|---|
| lipsă | după fiecare randare |
[] |
o dată, după montare |
[a, b] |
după montare și când a sau b se schimbă (comparate cu Object.is) |
Pui în listă tot ce folosește efectul din props și state. Regula ESLint react-hooks/exhaustive-deps te ajută.
You might not need an effect
Cea mai frecventă greșeală în React: efecte pentru lucruri care nu sunt sincronizare cu exteriorul.
| În loc de efect pentru... | Fă așa |
|---|---|
| a calcula date din state / props | calculează direct la randare |
| a reacționa la un click / submit | pune logica în event handler |
| a reseta state când se schimbă un prop | key={id} pe componentă |
| a aduce date de pe server | Server Component sau TanStack Query |
| a notifica părintele că s-a schimbat ceva | apelează callback-ul în handler-ul care a făcut schimbarea |
Întrebarea cheie: „Codul ăsta rulează pentru că componenta a apărut pe ecran, sau pentru că userul a făcut ceva?” Primul → efect. Al doilea → event handler.
Fetch în efect — dacă chiar trebuie
useEffect(() => {
const controller = new AbortController()
fetch(`/api/users/${id}`, { signal: controller.signal })
.then(r => r.json())
.then(setUser)
.catch(() => {})
return () => controller.abort() // anulează request-ul vechi dacă id se schimbă repede
}, [id])Fără cleanup ai o race condition: răspunsul vechi poate veni după cel nou și să-l suprascrie.
useRef — memorie fără re-randare
useState |
useRef |
|
|---|---|---|
| Schimbarea declanșează randare | da | nu |
| Citire în timpul randării | da | nu (doar în efecte / handler-e) |
| Pentru | ce apare pe ecran | referințe la DOM, id-uri de timer, valori „din culise” |
const inputRef = useRef<HTMLInputElement>(null)
<input ref={inputRef} />
inputRef.current?.focus()Strict Mode
În development, React montează → demontează → montează din nou fiecare componentă, ca să verifice că efectele au cleanup corect. Dacă ceva „rulează de două ori” și strică, efectului îi lipsește cleanup-ul. În producție rulează o singură dată.
Pe scurt
- Efect = sincronizare cu ceva din afara React-ului; cleanup obligatoriu când pornești ceva.
- Dependențele = tot ce folosește efectul.
- Calcule → la randare; reacții la acțiuni → handler-e; date → Server Components / TanStack Query.