State, derivare și reducer
useState corect, update-uri imutabile, state derivat și useReducer.
Actualizat
Ce este state
State = memoria unei componente: date care se schimbă în timp, din cauza interacțiunii (un input, un toggle, un contor). Când state-ul se schimbă, React re-randează componenta cu noua valoare.
De ce nu o variabilă simplă? O variabilă locală (let count = 0) se resetează la fiecare randare și schimbarea ei nu declanșează nicio actualizare a ecranului.
const [count, setCount] = useState(0)countvaloarea curentăsetCountfuncția care o schimbă și cere o re-randare0valoarea inițială
Props vs state
| Props | State | |
|---|---|---|
| Vine de la | părinte | componenta însăși |
| Se poate modifica în componentă | nu | da, prin setter |
| Rol | configurare din afară | memorie internă |
Cum funcționează setter-ul
setCount(count + 1) // valoare nouă
setCount(c => c + 1) // updater — primește valoarea cea mai recentă- Setter-ul nu schimbă variabila imediat; programează o randare nouă.
console.log(count)imediat după arată valoarea veche. - React grupează mai multe setări (batching) într-o singură randare.
- Când noua valoare depinde de cea veche → updater function:
setCount(c => c + 1)de 3 ori dă +3;setCount(count + 1)de 3 ori dă +1.
State-ul e imutabil
React compară state-ul vechi cu cel nou prin referință. Obiectele și array-urile le înlocuiești, nu le modifici — vezi Referințe și imutabilitate.
setUser({ ...user, name }) // ✓
setTodos(todos => [...todos, newTodo]) // ✓
user.name = name; setUser(user) // ✗ aceeași referință → nicio re-randareState derivat — nu-l stoca
Dacă o valoare se poate calcula din props sau state, calculeaz-o la randare. Nu o ține în alt useState.
// ✗ două surse de adevăr + un efect care le sincronizează
const [todos, setTodos] = useState([])
const [doneCount, setDoneCount] = useState(0)
// ✓ o singură sursă, restul derivat
const doneCount = todos.filter(t => t.done).lengthCalcule chiar costisitoare → useMemo (sau React Compiler, care memoizează automat).
Unde ține state-ul — tipuri și când
| Unde | Când | Exemplu |
|---|---|---|
useState local |
îl folosește o singură componentă | input, dropdown deschis |
| ridicat în părinte (lift state up) | doi frați au nevoie de el | filtru + listă |
URL (?page=2) |
trebuie partajabil și să supraviețuiască reload-ului | filtre, paginare, tab activ |
| Context | multe componente adânci, schimbări rare | tema, userul logat, limba |
| store (Zustand) | global, schimbat des | coș de cumpărături în SPA |
| cache de server state | date de pe server | Server state |
Regula: cât mai jos posibil, ridici doar când e nevoie.
useReducer — când logica crește
Când un state are multe moduri de a se schimba, centralizezi logica într-un reducer — o funcție pură (state, action) => newState:
function todosReducer(state, action) {
switch (action.type) {
case 'add': return [...state, action.todo]
case 'toggle': return state.map(t => (t.id === action.id ? { ...t, done: !t.done } : t))
case 'remove': return state.filter(t => t.id !== action.id)
default: return state
}
}
const [todos, dispatch] = useReducer(todosReducer, [])
dispatch({ type: 'add', todo })useState |
useReducer |
|---|---|
| 1–3 valori simple | state cu multe tranziții |
| logică în handler-e | logică într-un singur loc, testabilă separat |
Pe scurt
- State = memoria componentei; setter-ul cere o re-randare.
- Updater function când depinzi de valoarea veche; mereu obiecte noi.
- Derivă ce poți calcula; ține state-ul cât mai jos; URL pentru ce trebuie partajat.