0% au considerat acest document util (0 voturi)
0 vizualizări143 pagini

React Js

ReactJS este o bibliotecă JavaScript utilizată pentru construirea interfețelor de utilizator, în special pentru aplicații web moderne. Documentul detaliază concepte esențiale precum JSX, componente, props, useState, useRef, Context API, useReducer și useEffect, fiecare având roluri specifice în gestionarea stării și a interacțiunilor în aplicațiile React. Aceste instrumente permit dezvoltatorilor să creeze aplicații eficiente și reutilizabile, facilitând gestionarea datelor și a interacțiunilor utilizatorului.

Încărcat de

razvantaga97
Drepturi de autor
© All Rights Reserved
Respectăm cu strictețe drepturile privind conținutul. Dacă suspectați că acesta este conținutul dumneavoastră, reclamați-l aici.
Formate disponibile
Descărcați ca PDF, TXT sau citiți online pe Scribd
0% au considerat acest document util (0 voturi)
0 vizualizări143 pagini

React Js

ReactJS este o bibliotecă JavaScript utilizată pentru construirea interfețelor de utilizator, în special pentru aplicații web moderne. Documentul detaliază concepte esențiale precum JSX, componente, props, useState, useRef, Context API, useReducer și useEffect, fiecare având roluri specifice în gestionarea stării și a interacțiunilor în aplicațiile React. Aceste instrumente permit dezvoltatorilor să creeze aplicații eficiente și reutilizabile, facilitând gestionarea datelor și a interacțiunilor utilizatorului.

Încărcat de

razvantaga97
Drepturi de autor
© All Rights Reserved
Respectăm cu strictețe drepturile privind conținutul. Dacă suspectați că acesta este conținutul dumneavoastră, reclamați-l aici.
Formate disponibile
Descărcați ca PDF, TXT sau citiți online pe Scribd

ReactJS

Def! React (sau [Link]) este o bibliotecă JavaScript folosită pentru a construi
interfețe de utilizator (UI), în special pentru aplicații web moderne.

I. JSX
JSX (JavaScript XML) este o „extensie de sintaxă” pentru JavaScript folosită în
React care îți permite să scrii cod ce arată ca HTML direct în JavaScript.
JSX = JavaScript + HTML într-un singur loc
const element = <h1>Hello, world!</h1>;

JSX NU este înțeles direct de browser. El este transformat (compilat) în


JavaScript normal, de obicei cu un tool precum Babel.
De ce JSX?
1. React îmbrățișează faptul că logica de randare este inerent cuplată cu alte
logici ale interfeței utilizator: cum sunt gestionate evenimentele, cum se schimbă
starea în timp și cum sunt pregătite datele pentru afișare.
2. În loc să separe artificial tehnologiile prin plasarea markup-ului și a logicii în
fișiere separate, React separă aspectele cu unități slab cuplate numite
„componente” care le conțin pe ambele.

II. RENDERING ELEMENTS


Să presupunem că există un <div> undeva în fișierul HTML:
<div id="root"></div>
Numim acesta un nod DOM „rădăcină” deoarece tot ce se află în interiorul său va
fi gestionat de React DOM.
Aplicațiile construite doar cu React au de obicei un singur nod DOM rădăcină.
Dacă integrați React într-o aplicație existentă, puteți avea oricâte noduri DOM
rădăcină izolate doriți.

Pentru a reda un element React, transmiteți mai întâi elementul DOM către
[Link](), apoi transmiteți elementul React către [Link]():
const root = [Link](
[Link]('root')
);
const element = <h1>Salut, lume</h1>;
[Link](element);

III. COMPONENTS
Def! Conceptual, componentele sunt ca funcțiile JavaScript. Acestea acceptă
intrări arbitrare (numite „props”) și returnează elemente React care descriu ce ar
trebui să apară pe ecran.
Componentele vă permit să împărțiți interfața utilizator în părți independente,
reutilizabile și să vă gândiți la fiecare parte în mod izolat. Această pagină oferă o
introducere în ideea de componente. Puteți găsi o referință detaliată a API-ului
componentelor aici.

Definirea componentelor
Cea mai simplă metodă de a defini o componentă este de a scrie o funcție
JavaScript:
function Welcome(props) {
return <h1>Hello, {[Link]}</h1>;
}
Această funcție este o componentă React validă deoarece acceptă un singur
argument obiect „props” (care reprezintă proprietăți) cu date și returnează un
element React. Numim astfel de componente „componente funcție” deoarece
sunt literalmente funcții JavaScript.

Randarea componentelor
De exemplu, acest cod redă „Hello, Sara” pe pagină:
function Welcome(props) {
return <h1>Hello, {[Link]}</h1>;
}
const root = [Link]([Link]('root'));
const element = <Welcome name="Sara" />;
[Link](element);
Apelăm [Link]() cu elementul <Welcome name="Sara" />. React apelează
componenta Welcome cu {name: 'Sara'} ca prop. Componenta noastră Welcome
returnează un element <h1>Hello, Sara</h1> ca rezultat.
DOM-ul React actualizează eficient DOM-ul pentru a se potrivi cu <h1>Hello,
Sara</h1>.
Notă: Începeți întotdeauna numele componentelor cu o literă mare.
React tratează componentele care încep cu litere mici ca etichete DOM. De
exemplu, <div /> reprezintă o etichetă div HTML, dar <Welcome /> reprezintă o
componentă și necesită ca Welcome să fie în domeniul de aplicare.

Component composition
Componentele se pot referi la alte componente în rezultatul lor. Acest lucru ne
permite să folosim aceeași abstractizare a componentelor pentru orice nivel de
detaliu. Un buton, un formular, un dialog, un ecran: în aplicațiile React, toate
acestea sunt exprimate în mod obișnuit ca componente.

De exemplu, putem crea o componentă App care afișează mesajul Welcome de


mai multe ori:

function Welcome(props) {
return <h1>Hello, {[Link]}</h1>;
}

function App() {
return (
<div>
<Welcome name="Sara" />
<Welcome name="Cahal" />
<Welcome name="Edite" />
</div>
);
}
IV. PROPS
În React, props (prescurtare de la properties) sunt un obiect care conține datele
transmise unei componente din exterior.
Pe scurt, props = parametrii unei componente.

Când folosești o componentă, îi poți trimite valori:


<Welcome name="Sara" />
Aceste valori ajung în componentă sub forma unui obiect props:
function Welcome(props) {
return <h1>Hello, {[Link]}</h1>;
}

Caracteristici importante

 Props sunt read-only (nu trebuie modificate în componentă)


 Sunt transmise de la părinte → copil
 Pot conține orice: stringuri, numere, obiecte, funcții etc.

Props sunt folosite pentru:

 a transmite date între componente


 a face componentele reutilizabile
 a personaliza conținutul afișat

V. useState
Def! useState este un hook din React care permite componentelor funcționale să
aibă și să gestioneze stare (state).

useState îți permite să stochezi și să actualizezi date într-o componentă, iar


când aceste date se schimbă, componenta se reafișează automat.

Sintaxa
const [state, setState] = useState(valoareInitiala);
 state = valoarea curentă
 setState = funcția care actualizează valoarea
Exemplu simplu
function Counter() {
const [count, setCount] = useState(0);

return (
<>
<p>{count}</p>
<button onClick={() => setCount(count + 1)}>+</button>
</>
);
}

Cum funcționează

 La prima randare → count = 0


 Când apeși pe buton → setCount actualizează valoarea
 React re-randează componenta cu noua valoare

Idei importante

 useState păstrează date între randări


 orice modificare de stare → declanșează re-randare
 poate stoca orice tip de date (număr, string, obiect etc.)
 dacă noua valoare depinde de cea veche, poți folosi:

VI. useRef
Def: useRef este un hook React care vă permite să faceți referire la o valoare care
nu este necesară pentru randare.
Sintaxa: const ref = useRef(initialValue)
Returneaza
useRef returnează un obiect ref mutabil a cărui proprietate .current este
inițializată la argumentul transmis (initialValue). Obiectul returnat va persista pe
întreaga durată de viață a componentei.
useRef are doua roluri prinicipale:

1. Acces direct la elemente din DOM


import { useRef } from "react";
function InputFocus() {
const inputRef = useRef();

const focusInput = () => {


[Link]();
};
return (
<>
<input ref={inputRef} />
<button onClick={focusInput}>Focus</button>
</>
);
}
[Link] = elementul <input>

2. Stocare de valori între render-uri (fără re-render)


const countRef = useRef(0);

function increment() {
[Link]++;
[Link]([Link]);
}
modificarea lui [Link] NU declanșează re-render

Caracteristici importante:
 Persistă între render-uri -> Nu se resetează ca variabilele normale.
 Nu provoacă re-render -> Foarte important pentru performanța
 Este mutabil -> Poți modifica direct: [Link] = ceva;
 Referință stabilă -> Obiectul ref rămâne același între render-uri
Exemplu comun:
function TextInputWithFocusButton() {
const inputEl = useRef(null);
const onButtonClick = () => {
// `current` points to the mounted text input element
[Link]();
};
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}

useRef din React este ca o „cutie” care stochează o valoare mutabilă în current și
persistă între render-uri.

Este folosit frecvent pentru accesarea elementelor DOM (prin ref), dar poate
stoca orice valoare modificabilă, similar câmpurilor de instanță din clase.

Spre deosebire de un obiect obișnuit, useRef returnează același obiect la fiecare


randare, ceea ce îl face util pentru păstrarea datelor între render-uri.

Important: modificarea lui [Link] nu declanșează re-render. Pentru a


reacționa la atașarea/detașarea unui ref în DOM, se poate folosi un callback ref.

useImperativeHandle

useImperativeHandle(ref, createHandle, [deps])


useImperativeHandle personalizează valoarea instanței expusă componentelor
părinte atunci când se utilizează ref. Ca întotdeauna, codul imperativ care
utilizează refs ar trebui evitat în majoritatea cazurilor. useImperativeHandle ar
trebui utilizat cu forwardRef:
function FancyInput(props, ref) {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => {
[Link]();
}
}));
return <input ref={inputRef} ... />;
}
FancyInput = forwardRef(FancyInput);

În acest exemplu, o componentă părinte care redă <FancyInput ref={inputRef} />


ar putea apela [Link]().

VII. Context API

Def! Un Context API este un mecanism (în special cunoscut din React) care îți
permite să partajezi date între componente fără să mai treci manual props prin
fiecare nivel al aplicației („prop drilling”).

Componentele principale:
 creeateContext;
 Provider;
 Consumer;
 useContext;

createContext
- creează un obiect de tip „context” care va ține datele globale.
- createContext() returnează un obiect cu 2 lucruri importante:
o Provider
o Consumer (mai vechi, rar folosit azi)
- daca nu exista Provider se foloseste un defaultValue;
- const MyContext = createContext(defaultValue);

Provider
- este sursa datelor;
- infasoara aplicatia si trimie datele la copii;
- react creeaza un canal intern prin care provider trimite datele si toate
componentele din interior le pot citi;
- <[Link] value={...}>
{children}
</[Link]>
function App() {
const [user, setUser] = useState(null);
return (
<[Link] value={{ user, setUser }}>
<Dashboard />
</[Link]>
);
}

useContext
- Permite unei componente să citească valoarea din context.
- const value = useContext(MyContext);
- caută cel mai apropiat Provider, ia value si-l returneaza
function Profile() {
const { user } = useContext(UserContext);
return <h1>{user}</h1>;
}

Consumer

Def! Consumer este partea „veche” din Context API în React care îți permite să
citești datele din context fără să folosești hook-uri.
Consumer este folosit pentru a accesa valoarea din context.
<[Link]>
{value => <div>{value}</div>}
</[Link]>
Consumer folosește un pattern numit „render prop”: (value) => (...)

Această funcție:

 primește valoarea din Provider


 returnează ce vrei să afișezi
Exemplu:

const ThemeContext = createContext("light");


function App() {
return (
<[Link] value="dark">
<Navbar />
</[Link]>
);
}
function Navbar() {
return (
<[Link]>
{theme => <h1>Theme: {theme}</h1>}
</[Link]>
);
}

VIII. useReducer

Def! useReducer este un hook React care îți permite să adaugi un reducer
componentei tale. useReducer este o alternativă la useState pentru gestionarea
unui state mai complex.

Sintaxa
const [state, dispatch] = useReducer(reducer, initialState);

Parametri
reducer: Funcția reducer care specifică modul în care se actualizează starea.
Trebuie să fie pură, trebuie să ia starea și acțiunea ca argumente și trebuie să
returneze următoarea stare. Starea și acțiunea pot fi de orice tip.
initialState: Valoarea din care se calculează starea inițială. Poate fi o valoare de
orice tip. Modul în care se calculează starea inițială depinde de următorul
argument init.
opțional init: Funcția de inițializare care ar trebui să returneze starea inițială. Dacă
nu este specificată, starea inițială este setată la initialState. În caz contrar, starea
inițială este setată la rezultatul apelului init(initialState).

Returnează
useReducer returnează un array cu exact două valori:
Starea curentă. În timpul primei randări, este setată la init(initialState) sau
initialState (dacă nu există init).
Funcția de dispatch care vă permite să actualizați starea la o valoare diferită și să
declanșați o re-randare.

Exemplu complet
import { useReducer } from "react";
const initialState = { count: 0 };

function reducer(state, action) {


switch ([Link]) {
case "INCREMENT":
return { count: [Link] + 1 };
case "DECREMENT":
return { count: [Link] - 1 };
default:
return state;
}
}

function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<>
<p>{[Link]}</p>
<button onClick={() => dispatch({ type: "INCREMENT" })}>
+
</button>
<button onClick={() => dispatch({ type: "DECREMENT" })}>
-
</button>
</>
);}

IX. useEffect
Def! useEffect este un React Hook care vă permite să sincronizați o componentă
cu un sistem extern.

Sintaxa
useEffect(setup, dependencies?)

Parametrii
setup: Funcția cu logica Efectului. Funcția de configurare poate, de asemenea,
returna opțional o funcție de curățare. Când componenta se execută un commit,
React va rula funcția de configurare. După fiecare commit cu dependențe
modificate, React va rula mai întâi funcția de curățare (dacă ați furnizat-o) cu
valorile vechi, apoi va rula funcția de configurare cu noile valori. După ce
componenta este eliminată din DOM, React va rula funcția de curățare.

dependențe opționale: Lista tuturor valorilor reactive la care se face referire în


codul de configurare. Valorile reactive includ prop-uri, stare și toate variabilele și
funcțiile declarate direct în corpul componentei. Dacă linter-ul este configurat
pentru React, acesta va verifica dacă fiecare valoare reactivă este specificată
corect ca dependență. Lista de dependențe trebuie să aibă un număr constant de
elemente și să fie scrisă în linie, cum ar fi [dep1, dep2, dep3]. React va compara
fiecare dependență cu valoarea sa anterioară folosind comparația [Link]. Dacă
omiteți acest argument, Efectul va rula din nou după fiecare commit al
componentei. Vedeți diferența dintre transmiterea unui array de dependențe, a
unui array gol și a niciunei dependențe.

Returnează
useEffect returnează undefined.

Cele 3 cazuri de baza


Fără dependency array
useEffect(() => {
[Link]("rulează mereu");
});
Se execută: la fiecare render
Cu array gol []
useEffect(() => {
[Link]("rulează o singură dată");
}, []);
Se execută: doar la mount (prima randare)

Cu dependențe
useEffect(() => {[Link]("count s-a schimbat");}, [count]);
Se execută: la mount si ori de câte ori count se schimbă
Exemplu complet
import { useState, useEffect } from "react";
function App() {
const [count, setCount] = useState(0);
// 1. Rulează o singură dată (la mount)
useEffect(() => {
[Link]("Component mounted");
return () => {
[Link]("Component unmounted");
};
}, []);
// 2. Rulează când count se schimbă
useEffect(() => {
[Link]("Count changed:", count);
}, [count]);
// 3. Interval (cu cleanup)
useEffect(() => {
const interval = setInterval(() => {
[Link]("Timer running...");
}, 2000);
return () => {
clearInterval(interval);
[Link]("Interval cleared");
};
}, []);
return (
<div>
<h1>Count: {count}</h1>
<button onClick={() => setCount(count + 1)}>
Increment
</button>
</div>
);
}
export default App;

X. useCallback
Def! useCallback este un React Hook care vă permite să memorați în cache
definiția unei funcții între re-randări.

Sintaxa
const cachedFn = useCallback(fn, dependencies)

Parametri
fn: Valoarea funcției pe care doriți să o memorați în cache. Poate primi orice
argumente și poate returna orice valori. React va returna (nu va apela!) funcția
dvs. înapoi în timpul randării inițiale. La următoarele randări, React vă va oferi
aceeași funcție din nou dacă dependențele nu s-au modificat de la ultima
randare. În caz contrar, vă va oferi funcția pe care ați transmis-o în timpul randării
curente și o va stoca în cazul în care poate fi reutilizată ulterior. React nu va apela
funcția dvs. Funcția vă este returnată, astfel încât să puteți decide când și dacă o
apelați.

dependențe: Lista tuturor valorilor reactive la care se face referire în codul fn.
Valorile reactive includ prop-uri, stare și toate variabilele și funcțiile declarate
direct în corpul componentei. Dacă linter-ul dvs. este configurat pentru React,
acesta va verifica dacă fiecare valoare reactivă este specificată corect ca o
dependență. Lista de dependențe trebuie să aibă un număr constant de elemente
și să fie scrisă în linie, cum ar fi [dep1, dep2, dep3]. React va compara fiecare
dependență cu valoarea sa anterioară folosind algoritmul de comparație
[Link].

Returnează
La randarea inițială, useCallback returnează funcția fn pe care ați transmis-o.
În timpul randărilor ulterioare, va returna fie o funcție fn deja stocată de la ultima
randare (dacă dependențele nu s-au modificat), fie va returna funcția fn pe care
ați transmis-o în timpul acestei randări.

Exemplu complet

fara useCallback
function Parent() {
const [count, setCount] = useState(0);
const handleClick = () => {
[Link]("clicked");
};
return <Child onClick={handleClick} />;
}
cu useCallback
const handleClick = useCallback(() => {
[Link]("clicked");
}, []);

XI. FetchData

Exemplu simplu:
import { useEffect, useState } from "react";
function Users() {
const [users, setUsers] = useState([]);
useEffect(() => {
fetch("[Link]
.then(response => [Link]())
.then(data => setUsers(data))
.catch(error => [Link](error));
}, []);
return (
<div>
<h1>Lista utilizatori</h1>
{[Link](user => (
<p key={[Link]}>{[Link]}</p>
))}
</div>
);
}
export default Users;

 const [users, setUsers] = useState([]); -> stocheaza datele primite de la API;


 useEffect(() => { ... }, []); -> ruleaza o singura data;
 fetch(url) -> face request HTTP (GET implicit)
o then(response => [Link]) -> trasforma raspunsul in JSON
o setUsers(data) -> salveaza datele -> declanseaza re-render

Exempu cu async/await
useEffect(() => {
async function fetchUsers() {
try {
const response = await fetch("[Link]
const data = await [Link]();
setUsers(data);
} catch (error) {
[Link](error);
}
}

fetchUsers();
}, []);

Exemplu cu loading si error:


const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);

useEffect(() => {
async function fetchUsers() {
try {
const res = await fetch("[Link]
const data = await [Link]();
setUsers(data);
} catch (err) {
setError(err);
} finally {
setLoading(false);
}
}

fetchUsers();
}, []);
if (loading) return <p>Se încarcă...</p>;
if (error) return <p>Eroare!</p>;

Exemplu cu axios:
npm install axios
import axios from "axios";
useEffect(() => {
[Link]("[Link]
.then(res => setUsers([Link]))
.catch(err => [Link](err));
}, []);

XII. Custom Hook

Def: Un custom hook este o functie javascritp care:


 al cărei nume începe cu “use”
 care folosește alte hook-uri (useState, useEffect, etc.)
 și îți permite să reutilizezi logică între componente
Exemplu simplu cu fetch:
import { useEffect, useState } from "react";
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);

useEffect(() => {
async function getData() {
try {
const res = await fetch(url);
const json = await [Link]();
setData(json);
} catch (err) {
setError(err);
} finally {
setLoading(false);
}
}
getData();
}, [url]);
return { data, loading, error };
}
export default useFetch;

Cum se foloseste?
function Users() {
const { data, loading, error } = useFetch(
"[Link]
);
if (loading) return <p>Se încarcă...</p>;
if (error) return <p>Eroare!</p>;
return (
<div>
{[Link](user => (
<p key={[Link]}>{[Link]}</p>
))}
</div>
);

In React, hook-urile nu comunica direct intre ele. Ele returneaza valori si primesc
parametrii. Comunicatia se face prin props, return values sau context.
 Transmiterea valorilor prin parametri și return
o Cel mai simplu mod: un hook primește valori ca parametru și
returnează valori. Apoi alt hook sau componentă folosește return-ul.
 Folosirea unui hook comun pentru multiple componente sau hook-uri
o Poți crea un hook care gestionează state-ul, iar alte hook-uri sau
componente îl folosesc prin return.
 Folosirea Context pentru valori globale între hook-uri
o Dacă mai multe hook-uri sau componente trebuie să acceseze
aceeași valoare, folosești React Context.

XIII. Memoization
Def! Memoizarea (memoization) este o tehnică de optimizare prin care salvezi
rezultatul unei funcții pentru anumite intrări (input-uri), astfel încât dacă
funcția este apelată din nou cu aceleași intrări, în loc să recalculeze rezultatul, îl
returnează din memorie.
Este un concept general din informatică, nu unul specific React. useMemo este
doar implementarea acestui concept în React.

De ce avem nevoie de memoizare?


Imaginează-ți o funcție care calculează factorialul unui număr.

function factorial(n) {
[Link]("Calculez...");
if (n <= 1) return 1;
return n * factorial(n - 1);
}

Dacă o apelezi, factorial(10); se execută toate calculele.


Dacă o apelezi din nou, factorial(10); JavaScript recalculează totul, chiar dacă
rezultatul este același.

Ideea de baza: În loc să recalculezi, salvezi rezultatul.

Cum se implementează

Fara memoizare:
function square(x) {
[Link]("Calculez");
return x * x;
}
square(5);
square(5);
square(5);

Output:
Calculez
Calculez
Calculez
Cu memoizare:
const cache = {};
function square(x) {
if (cache[x]) {
[Link]("Din cache");
return cache[x];
}
[Link]("Calculez");
const result = x * x;
cache[x] = result;
return result;
}

Prima data: square(5) -> calculez


A doua oara: square(5) -> din cache
XIV. [Link]
Def! [Link] este o metodă de optimizare (o componentă de ordin superior
- Higher-Order Component) folosită în React pentru a evita randările inutile ale
componentelor funcționale.
Atunci când o componentă părinte își schimbă starea, toate componentele ei
copil sunt randate din nou în mod implicit. Dacă un component copil nu depinde
de acea stare și are aceleași props (proprietăți), randarea sa repetată este o risipă
de resurse.

Cum funcționează React fără [Link]

Să presupunem că ai două componente:


function App() {
const [count, setCount] = useState(0);
return (
<>
<button onClick={() => setCount(count + 1)}>
+
</button>

<Child />
</>
);
}

function Child() {
[Link]("Child render");
return <h1>Salut</h1>;
}

Primul render
React executa: App -> Child -> in consola: Child render.

Apesi pe buton: setCount(count+1)


React face: state schimbat -> app se re-randeaza -> chil se re-randeaza
React presupune ca, daca parintele s-a re-randat si copiii trebuie reevaluati.
Cum intervine [Link]
import { memo } from "react";

const Child = memo(function Child() {


[Link]("Child render");

return <h1>Salut</h1>;
});

Primul render: App -> Child


Apesi pe buton: App -> Child?
Inainte de a executa Child, React compara props-urile vechi cu props-urile noi.
Cum Child nu primeste nimic => props vheci = {} si props nou = {} nu mai executa
functia Child.

[Link] memorează rezultatul randării unei componente, nu funcția și nici


datele.

Cum funcționează
 Memorare (Caching): Când învelești o componentă în [Link], React îi
memorează rezultatul (JSX-ul) la prima randare.
 Comparația Props-urilor: La următoarele randări, React va compara noile props
cu cele vechi.
 Decizia de Randare: Dacă props-urile nu s-au schimbat, React va folosi
componenta din memorie, sărind complet peste procesul de re-randare.

Când trebuie folosit


 În componente care au liste foarte mari (ex. tabele, galerii).
 În componente care necesită calcule foarte scumpe sau complexe la fiecare
randare.
 Când o componentă primește aceleași props de mii de ori.
Când NU trebuie folosit
 Pentru componente foarte simple care se randează aproape instantaneu.
Procesul de comparare a props-urilor aduce un cost suplimentar de memorie, deci
poate face aplicația mai lentă în loc de mai rapidă.
 Dacă transmiți funcții sau obiecte ca props, pentru că acestea își schimbă adresa
de memorie la fiecare randare (se creează noi instanțe). În acest caz, optimizarea
se va "rupe". Pentru a o repara, trebuie să folosești și hook-urile useCallback și
useMemo.

XV. useMemo

Def! useMemo este un Hook din React folosit pentru a memora (memoize)
rezultatul unui calcul costisitor, astfel încât acel calcul să nu fie refăcut la fiecare
re-randare a componentei.

Cum funcționează useMemo


const valoareMemorata = useMemo(() => {
// calcul
return rezultat;
}, [dependente]);

Are două argumente:


1. O funcție care returnează valoarea calculată.
2. Un array de dependențe.

React:
 execută funcția la primul render;
 salvează rezultatul;
 la render-urile următoare verifică dependențele;
 dacă acestea NU s-au schimbat, returnează valoarea memorată;
 dacă s-au schimbat, execută din nou funcția și salvează noul rezultat.

Exemplu:
function App() {
const [count, setCount] = useState(0);
const numbers = [1,2,3,4,5];
const sum = [Link]((a,b)=>a+b,0);
return (
<>
<h1>{count}</h1>
<p>{sum}</p>
<button onClick={() => setCount(count + 1)}>+</button>
</>
);
}

La fiecare click:
 componenta se re-randează;
 reduce() se execută din nou.
În acest caz nu contează deoarece calculul este foarte mic.

Cu useMemo:
const sum = useMemo(() => {
return [Link]((a,b)=>a+b,0);
}, [numbers]);

Acum:
 primul render → calculează suma;
 dacă numbers nu se schimbă → React returnează suma memorată;
 nu mai execută reduce().

Un exemplu mai realist

Imaginează-ți că ai 100.000 produse.


const products = [...100000 produse];

Trebuie să filtrezi produsele.


const filteredProducts = [Link](product =>
[Link] > 500 &&
[Link] === selectedCategory
);

Dacă utilizatorul schimbă doar tema aplicației:


setDarkMode(true);

Componenta se re-randează.
Fără useMemo, React execută iar filtrarea celor 100.000 produse.

cu useMemo
const filteredProducts = useMemo(() => {
return [Link](product =>
[Link] > 500 &&
[Link] === selectedCategory
);
}, [products, selectedCategory]);

Acum filtrarea se face doar când:


 se schimbă lista de produse;
 se schimbă categoria.

Când merită folosit?


Da:
 calcule costisitoare (sortări, filtrări, agregări complexe);
 transformări pe liste mari;
 obiecte sau array-uri transmise către componente optimizate cu
[Link];
 valori care sunt scumpe de calculat și se schimbă rar.
Nu:
 pentru calcule triviale (a + b, concatenări simple de șiruri);
 pentru orice variabilă doar „ca să optimizezi”;
 dacă nu există o problemă reală de performanță.

Reguli importante
 Include toate valorile folosite în funcția de calcul în lista de dependențe.
 Nu folosi useMemo ca substitut pentru gestionarea stării (useState) sau a
efectelor (useEffect).
 Optimizează doar atunci când există un motiv: de exemplu, ai observat re-
randări inutile sau calcule costisitoare.

XVI. Forms
În React, formularele sunt folosite pentru a colecta date de la utilizator (input-uri,
checkbox-uri, select-uri etc.). Ele se diferențiază de HTML simplu prin faptul că
React gestionează starea valorilor introduse, nu doar DOM-ul.

Există două tipuri principale:

1. Controlled Components (componente controlate) – React controlează


complet valoarea input-ului.
2. Uncontrolled Components (componente necontrolate) – valoarea input-
ului e gestionată de DOM și se citește doar la nevoie.

Preluarea datelore folosind useState


În React, useState ține evidența valorilor input-urilor. Astfel, în loc să citim
direct din DOM, fiecare input „spune” React-ului ce valoare are.

Structura generală: const [valoare, setValoare] = useState('');


 valoare = starea curentă a input-ului
 setValoare = funcția care actualizează starea
 useState('') = valoarea inițială a input-ului

Formular cu un singur input:


import { useState } from 'react';
function Formular() {
const [nume, setNume] = useState(''); // inițial e gol
const handleChange = (event) => {
setNume([Link]); // preluăm ce scrie utilizatorul
};
const handleSubmit = (event) => {
[Link](); // nu vrem reload
[Link]('Datele introduse:', nume); // aici preluăm datele
};
return (
<form onSubmit={handleSubmit}>
<label>
Nume:
<input type="text" value={nume} onChange={handleChange} />
</label>
<button type="submit">Trimite</button>
</form>
);
}
export default Formular;

Explicație:

1. value={nume} – input-ul e „controlat” de React.


2. onChange={handleChange} – la fiecare tastă apăsată, valoarea input-ului
actualizează starea.
3. handleSubmit – când utilizatorul trimite formularul, nume conține exact ce
a scris.

Formular cu mai multe inputuri:


import { useState } from 'react';
function FormularComplex() {
const [form, setForm] = useState({ email: '', parola: '' });
const handleChange = (event) => {
const { name, value } = [Link];
setForm({ ...form, [name]: value }); // actualizează doar câmpul modificat
};
const handleSubmit = (event) => {
[Link]();
[Link]('Datele introduse:', form);
};
return (
<form onSubmit={handleSubmit}>
<input type="email" name="email" value={[Link]}
onChange={handleChange} placeholder="Email"/>
<input type="password" name="parola" value={[Link]}
onChange={handleChange} placeholder="Parolă" />
<button type="submit">Trimite</button>
</form>
);}
export default FormularComplex;

Explicație:

 name="email" și name="parola" permit să știm care câmp s-a modificat.


 [name]: value actualizează doar acel câmp în obiectul de stare.
 ...form păstrează valorile celorlalte câmpuri.

Preluarea datelore folosind useRef


useRef creează o referință către un element DOM: const inputRef = useRef();
 [Link] va fi elementul HTML real.
 De aici putem citi valoarea input-ului: [Link].

import { useRef } from 'react';


function FormularNecontrolat() {
const numeRef = useRef(); // creează referința
const handleSubmit = (event) => {
[Link]();
alert(`Ai introdus: ${[Link]}`); // preluăm valoarea
};
return (
<form onSubmit={handleSubmit}>
<label>
Nume:
<input type="text" ref={numeRef} />
</label>
<button type="submit">Trimite</button>
</form>
);
}
export default FormularNecontrolat;

Explicație pas cu pas:


1. const numeRef = useRef() – creează referința pentru input.
2. ref={numeRef} – leagă input-ul de referință.
3. [Link] – citește valoarea input-ului direct din DOM, doar
la nevoie.
4. Nu folosim useState, deci React nu „controlează” valoarea input-ului.
import { useRef } from 'react';

function FormularMultiRef() {
const emailRef = useRef();
const parolaRef = useRef();

const handleSubmit = (event) => {


[Link]();
[Link]('Email:', [Link]);
[Link]('Parola:', [Link]);
};
return (
<form onSubmit={handleSubmit}>
<input type="email" ref={emailRef} placeholder="Email" />
<input type="password" ref={parolaRef} placeholder="Parolă" />
<button type="submit">Trimite</button>
</form>
);
}

export default FormularMultiRef;

Avantaje și dezavantaje:
 Mai simplu pentru formulare mici.
 Nu declanșează re-render la fiecare tastă (spre deosebire de useState).
 Mai greu de validat în timp real, deoarece valorile nu sunt în React state.

Preluarea datelore folosind FormData


FormData permite să citești toate câmpurile unui formular dintr-un element
<form> și să le prelucrezi ca un obiect.

 new FormData(formElement) → creează un obiect cu toate datele


formularului.
 [Link]('nume_camp') → obții valoarea unui câmp după name.

function FormularFormData() {
const handleSubmit = (event) => {
[Link](); // prevenim reload-ul paginii
const form = [Link]; // elementul <form>
const formData = new FormData(form); // creăm FormData
const nume = [Link]('nume'); // preluăm valoarea câmpului "nume"
alert(`Ai introdus: ${nume}`);
};
return (
<form onSubmit={handleSubmit}>
<label>
Nume:
<input type="text" name="nume" />
</label>
<button type="submit">Trimite</button>
</form>
);
}
export default FormularFormData;

Explicație:
1. [Link] = formularul trimis.
2. new FormData(form) = obiect cu toate câmpurile din form.
3. [Link]('nume') = obții valoarea câmpului cu name="nume".

function FormularMultiFormData() {
const handleSubmit = (event) => {
[Link]();
const form = [Link];
const formData = new FormData(form);
const email = [Link]('email');
const parola = [Link]('parola');
[Link]('Email:', email);
[Link]('Parola:', parola);
};
return (
<form onSubmit={handleSubmit}>
<input type="email" name="email" placeholder="Email" />
<input type="password" name="parola" placeholder="Parolă" />
<button type="submit">Trimite</button>
</form>
);
}
export default FormularMultiFormData;

Avantaje:
 Nu ai nevoie de useState sau useRef.
 Poți prelua rapid date din formulare complexe, inclusiv fișiere (<input
type="file">).
 Funcționează și pentru formularul complet fără să legi fiecare input de
state.
Dezavantaje:
 Nu poți valida sau afișa instant valori, pentru că React nu „vede” valorile
până la submit.
 Mai puțin flexibil pentru interacțiuni dinamice în timp real.
Resetare datelor unui formular:
 folosind useState -> setForm({ nume: '', email: '' }); // resetare state → input-
urile devin goale
 folosind useRef -> [Link] = '' resetează input-ul fără state.
 folosind FormData -> FormData doar citește valorile, deci pentru resetare
trebuie să folosești metoda nativă [Link]():

Validarea datelor din formular


 useState:
// validare live
if ([Link]() === '') {
setErrors((prev) => ({ ...prev, [name]: 'Acest câmp este obligatoriu' }));
} else if (name === 'email' && !/\S+@\S+\.\S+/.test(value)) {
setErrors((prev) => ({ ...prev, email: 'Email invalid' }));
} else {
setErrors((prev) => ({ ...prev, [name]: '' }));
}
};
const handleSubmit = (e) => {
[Link]();
// validare finală la submit
let valid = true;
const newErrors = {};
if ([Link]() === '') {
[Link] = 'Nume obligatoriu';
valid = false;
}
if (!/\S+@\S+\.\S+/.test([Link])) {
[Link] = 'Email invalid';
valid = false;
}
setErrors(newErrors);
if (valid) {
[Link]('Formular valid:', form);
setForm({ nume: '', email: '' }); // resetare
}
};
 useRef:
const nume = [Link]();
if (!nume || !/\S+@\S+\.\S+/.test(email)) {
setError('Nume și email trebuie completate corect');
return;
}

 FormData:
const nume = [Link]('nume').trim();
const email = [Link]('email').trim();

if (!nume || !/\S+@\S+\.\S+/.test(email)) {
alert('Formular invalid');
return;
}

XVII. useActionState
useActionState este un hook React care leagă o acțiune a utilizatorului
(ex. trimiterea unui formular) de o stare specifică. El actualizează starea automat
pe baza rezultatului acțiunii.
Sintaxa de bază:
const [state, formAction, isPending] = useActionState(actionFn, initialState);
 actionFn – funcția care rulează la acțiunea utilizatorului, primind starea
curentă și datele formularului.
 initialState – valoarea inițială a stării (obiect, string, număr etc.).

Ce returnează hook-ul:

 state – starea curentă, actualizată după fiecare acțiune.


 formAction – legătura între <form> și logica din actionFn.
 isPending – boolean care indică dacă acțiunea este în desfășurare (util
pentru loading sau dezactivarea butoanelor).

Avantaje principale:
 Reduce boilerplate-ul (nu mai trebuie state separate pentru loading, erori
sau valori trimise).
 Simplifică actualizările asincrone fără useEffect.
 Îmbunătățește performanța prin gestionarea eficientă a stării și evitarea re-
render-urilor inutile.

Exemplu:
"use client";

import { useActionState } from "react";


// submit form action
async function submitForm(prevState, formData) {
await new Promise((resolve) => setTimeout(resolve, 1500));
const email = [Link]("email");
if (!email || ![Link]("@")) {
return { success: false, message: "Please enter a valid email address." };
}
return { success: true, message: "Form submitted successfully!" };
}

function FormApp() {
const [state, formAction, isPending] = useActionState(submitForm, {
success: null,
message: "",
});

return (
<div className="form-container">
<div className="form-card">
<form action={formAction}>
<input
className="form-input"
type="text"
name="name"
placeholder="Name"
/>
<input
className="form-input"
type="email"
name="email"
placeholder="Email"
/>
<button className="form-button" disabled={isPending}>
{isPending ? "Submitting..." : "Submit"}
</button>
{[Link] && (
<p
className={`form-message ${[Link] ? "success" : "error"}`}
>
{[Link]}
</p>
)}
</form>
</div>
</div>
);
}

export default FormApp;

Form Submission cu useActionState


useActionState excelează în gestionarea trimiterii formularelor, mai ales în
scenarii reale care implică operațiuni asincrone (ex. apeluri API), stări de încărcare
și feedback pentru utilizator.

În exemplul dat, un formular simulează trimiterea cu întârziere și afișează un


mesaj de succes sau eroare fără a folosi stări suplimentare sau useEffect.

const [state, formAction, isPending] = useActionState(submitForm, { success: null,


message: "" });
 state – conține rezultatul acțiunii (success/message).
 formAction – legătura directă cu <form> pentru trimitere.
 isPending – gestionează starea de încărcare a butonului.

XVIII. useForm

Def! useForm este un hook pentru gestionarea formularelor în React, folosit în


special din librăria React Hook Form.

Ce problemă rezolvă useForm?


În React „simplu”, formularele devin rapid complicate:

 trebuie să ții state pentru fiecare input (useState)


 trebuie să validezi manual inputurile
 trebuie să gestionezi erori
 trebuie să controlezi re-render-uri frecvente

Ce face useForm?
useForm centralizează tot managementul formularului:

 colectează valorile inputurilor


 gestionează validarea
 gestionează erorile
 optimizează re-render-urile
 oferă metode de submit/reset/watch
Import și inițializare

import { useForm } from "react-hook-form";


function MyForm() {
const {
register,
handleSubmit,
formState: { errors },
} = useForm();
}

Cele mai importante lucruri din useForm


 register
<input {...register("email")} />
o Leagă input-ul de formular.
o colectează valoarea automat
 handleSubmit
o Funcția care gestionează submit-ul.
const onSubmit = (data) => {
[Link](data);
};
<form onSubmit={handleSubmit(onSubmit)}>
{
email: "test@[Link]",
password: "123456"
}
 [Link]
o Ține erorile de validare:
{[Link] && <p>Email invalid</p>}

Exemplu complet simplu

import { useForm } from "react-hook-form";


function LoginForm() {
const {
register,
handleSubmit,
formState: { errors },
} = useForm();
const onSubmit = (data) => {
[Link]("Login data:", data);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input
{...register("email", { required: "Email obligatoriu" })}
placeholder="Email"
/>
{[Link] && <p>{[Link]}</p>}

<input
type="password"
{...register("password", { minLength: 6 })}
placeholder="Parolă"
/>
{[Link] && <p>Parola este prea scurtă</p>}

<button type="submit">Login</button>
</form>
);
}

Validari
register("email", {
required: "Email obligatoriu",
pattern: {
value: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
message: "Email invalid",
},
});
watch
const { watch } = useForm();
const email = watch("email");
- randeaza componenta de fiecare data cand value se schimba;
reset
const { reset } = useForm();
reset();
- reseteaza valorile din form;

Controller

În React, elementele de formular se împart în două categorii: native (input, radio,


checkbox, select HTML standard) și custom (librării complexe precum React-
Select, date-pickere sau editoare de text).

Acel <Controller /> din react-hook-form acționează ca un translator (sau pod de


legătură) între modul în care funcționează o componentă custom și modul în
care react-hook-form colectează datele.

Iată exact de ce avem nevoie de el, în 3 puncte simple:


1. Problema cu register din React Hook Form
Când folosești {...register("firstName")} pe un input nativ din HTML, React Hook
Form injectează în acel input câteva proprietăți standard:
 name="firstName"
 onChange={...} (care caută un eveniment nativ de browser unde valoarea
se află în [Link])
 onBlur={...}
 ref={...}
Componenta <Select /> din react-select nu este un input HTML nativ. Ea este un
pachet complex format din mai multe div-uri, butoane, text încapsulat și iconițe
SVG pentru a arăta bine.
Dacă ai pune {...register("hobbies")} direct pe <Select />, codul ar crăpa deoarece
react-select nu trimite un eveniment nativ de browser (cu [Link]) atunci
când selectezi o opțiune. Ea trimite direct un obiect sau un array de obiecte (ex:
[{value: 'hobby1', label: 'Hobby 1'}]).
2. Cum rezolvă <Controller /> această problemă?
Aici intervine <Controller />. El preia controlul asupra stării acelei componente
externe.
Proprietatea sa principală este render, care îți oferă un argument numit field.
Când scrii {...field} în interiorul lui <Select />, tu îi pasezi de fapt componentei 4
lucruri customizate:
 [Link]: Valoarea curentă salvată în formular (astfel dropdown-ul știe
ce să afișeze ca fiind selectat).
 [Link]: O funcție specială creată de react-hook-form. Când alegi
ceva în dropdown, react-select apelează această funcție și îi dă direct
datele selectate, iar react-hook-form le salvează imediat în starea sa
internă.
 [Link]: Anunță formularul când utilizatorul a dat click în afara
dropdown-ului (util pentru a declanșa validarea atunci când "părăsești"
câmpul).
 [Link]: Permite formularului să pună focusul automat pe acel dropdown
dacă utilizatorul încearcă să trimită formularul gol și validarea eșuează.

3. Metodă vizuală: Ce se întâmplă în spate


Fără Controller (Eroare):
 React-Select (zice: "Am selectat Hobby 1!") --> register (așteaptă un
eveniment HTML cu [Link]) --> Eroare / Date pierdute.
Cu Controller (Funcționează):
 React-Select (zice: "Am selectat Hobby 1!") --> [Link](date) -->
Controller (traduce datele pe înțelesul formularului) --> react-hook-form
(salvează cu succes).

Când ești obligat să folosești <Controller />?


Trebuie să îl folosești de fiecare dată când componenta din formular nu este un
element HTML simplu. Exemple comune:
 Librării de Dropdown (React-Select, MUI Select, Ant Design Select).
 Librării pentru selectat date (React-DatePicker).
 Switch-uri sau slidere custom (ex: un slider pentru volum sau preț).
 Editoare de text complexe (ex: CKEditor, Quill).

XIV. useFormStatus
Def! useFormStatus este un hook din React, introdus în special în contextul
formularelor din React Server Components sau cu framework-uri ca Remix sau
[Link] 13+, care te ajută să urmărești starea unui formular (ex. dacă este în
proces de trimitere). Practic, îți permite să faci UI mai reactiv la starea
formularului fără să gestionezi manual loading sau submitting.

useFormStatus returnează un obiect cu starea curentă a formularului.

Cele mai comune proprietăți sunt:


const { pending, submitting } = useFormStatus();
 pending – true dacă formularul este în curs de procesare (oricum ar fi
implementată trimiterea).
 submitting – true dacă formularul a fost efectiv trimis și așteaptă un
răspuns.

Diferența dintre pending și submitting:


 submitting: exact momentul când formularul a fost trimis, până primește
un răspuns.
 pending: poate include și alte stări de așteptare (ex. mutații server-side) –
depinde de implementarea framework-ului.

Exemplu
'use client'; // necesar în [Link] 13+
import { useFormStatus } from 'react';
function MyForm() {
const { pending, submitting } = useFormStatus();
return (
<form action="/api/submit" method="post">
<input type="text" name="username" />
<button type="submit" disabled={submitting}>
{submitting ? 'Se trimite...' : 'Trimite'}
</button>
{pending && <p>Formularul se procesează...</p>}
</form>
);
}
Ce se întâmplă aici:
 Când utilizatorul dă click pe „Trimite”, submitting devine true.
 Butonul se dezactivează și textul se schimbă.
 Dacă ai nevoie de un mesaj global „se procesează”, poți folosi pending.

XIX. Modal
Def! În React, „modalele” (modals) nu sunt o funcționalitate built-in a React, ci un
pattern de UI: o fereastră care apare peste conținutul principal și blochează sau
evidențiază o acțiune (confirmare, formular, detalii etc.).

Ce este un modal?
Un modal este o „fereastră superioară” (overlay) care:

 apare peste pagina curentă


 de obicei întunecă fundalul (backdrop)
 cere o acțiune (confirmare, input, alegere)
 blochează interacțiunea cu restul paginii până îl închizi

Tipuri de modale

1. Modal simplu
Cum funcționează

 ai un useState (true/false)
 când true → componenta modal este randată
 când false → nu există în DOM
Exemplu complet
function App() {
const [isOpen, setIsOpen] = useState(false);
return (
<div>
<button onClick={() => setIsOpen(true)}>
Deschide modal
</button>

{isOpen && (
<div className="modal">
<h2>Modal simplu</h2>
<button onClick={() => setIsOpen(false)}>
Închide
</button>
</div>
)}
</div>
);
}

Avantaje
 foarte ușor de înțeles
 rapid de implementat
 zero dependențe

Dezavantaje

 poate cauza probleme de CSS (z-index)


 nu are overlay bine structurat
 greu de reutilizat în aplicații mari
 nu are animații sau accesibilitate implicit

2. Modal cu portal
Modalul este randat în alt loc din DOM (de obicei în [Link]), nu în
ierarhia componentelor.
De ce?
 evită probleme de z-index
 nu este afectat de CSS-ul părintelui
 se pune direct în [Link]

import { createPortal } from "react-dom";


function Modal({ children, onClose }) {
return createPortal(
<div className="backdrop" onClick={onClose}>
<div className="modal" onClick={(e) => [Link]()}>
{children}
</div>
</div>,
[Link]
);
}

Cum funcționează

 React „mută” render-ul în alt nod DOM


 logica rămâne în React tree
 UI-ul este separat fizic

Avantaje
 nu depinde de layout-ul paginii
 rezolvă probleme de CSS
 standard în aplicații mari
 ușor de combinat cu animații

Dezavantaje
 puțin mai complex
 necesită înțelegerea DOM-ului
 debugging ușor mai dificil

Functia stopPropagation
 Ce face: Oprește propagarea unui eveniment (ex: click) în sus, către
elementele părinte.
 Problema rezolvată: Previne declanșarea accidentală a funcțiilor de pe
părinți atunci când interacționezi cu un element copil (ex: închideri
accidentale de modale/drop-down-uri).
 Sintaxă: onClick={(e) => [Link]()}

Conceptul Teoretic: Ce este Event Bubbling?


Pentru a înțelege stopPropagation, trebuie înțeles fenomenul de Event Bubbling
(Propagarea Evenimentelor).
 Când un eveniment (click, hover, keydown) se produce pe un element, browserul
execută handler-ul de pe acel element.
 Ulterior, evenimentul "urcă" (se propagă ca o bulă de aer) la părintele direct, apoi
la bunicul acestuia, până ajunge la rădăcina documentului (window).
 Fără această funcție, interfețele complexe (Modale, Dropdown-uri, Carduri cu
butoane interne) devin disfuncționale.

// ❌ CLICK-ul pe buton va declanșa și funcția de pe CARD!


<div id="card-parinte" onClick={deschidePaginaProdus}>
<h3>Nume Produs</h3>
<button id="buton-copil" onClick={adaugaInCos}>
Adaugă în Coș
</button>
</div>

 Rezultat nedorit: Când utilizatorul apasă „Adaugă în Coș”, produsul este


adăugat, dar imediat se deschide și pagina produsului, deoarece click-ul a urcat la
#card-parinte.
 Prin apelarea metodei, tăiem legătura de propagare. Evenimentul este procesat
doar de elementul pe care s-a dat click.

<div id="card-parinte" onClick={deschidePaginaProdus}>


<h3>Nume Produs</h3>
<button
id="buton-copil"
onClick={(e) => {
[Link](); // 🛡️ Oprește evenimentul aici
adaugaInCos();
}}
>
Adaugă în Coș
</button>
</div>

Cazuri de Utilizare Comune în Proiecte


1. Modale / Pop-up-uri: Click-ul pe conținutul modalului nu trebuie să declanșeze
funcția de închidere mapată pe overlay-ul din spate.
2. Meniuri Dropdown / Accordion: Click-ul pe o opțiune internă nu trebuie să
închidă sau să reseteze întregul meniu.
3. Butoane de ștergere în liste: Click-ul pe iconița de „Șterge” dintr-un rând de
tabel nu trebuie să deschidă detaliile acelui rând.

3. Modal controlat global

Ce este? Modalul nu mai este controlat local, ci dintr-un loc global:


 React Context
 Redux
 Zustand

De ce e util? În aplicații mari, vrei să poți:


 deschide modal din orice componentă
 evita prop drilling (setIsOpen peste tot)

Exemplu:
const ModalContext = createContext();
function ModalProvider({ children }) {
const [modal, setModal] = useState(null);
return (
<[Link] value={{ modal, setModal }}>
{children}
{modal && <Modal>{modal}</Modal>}
</[Link]>
);
}

Avantaje
 control centralizat
 foarte scalabil
 bun pentru aplicații mari

Dezavantaje
 mai mult boilerplate
 debugging mai complex
 poate deveni „overengineering” în proiecte mici

4. Modal din librării UI (pre-built)


Ce este
Folosirea de componente gata făcute din librării.

Cum funcționează
Librăria îți oferă:

 modal deja cu portal


 animații
 accesibilitate (ARIA)
 focus trap
 ESC handling

Exemplu:
<Modal show={show} onHide={onClose} centered size="lg">
<[Link] closeButton className="bg-primary text-white">
<[Link]>Date personale</[Link]>
</[Link]>

<[Link]>
<div className="border rounded p-3 bg-light">
<p><strong>Nume:</strong> {personalData?.inputName} </p>
<p><strong>Prenume:</strong> {personalData?.inputPrenume}</p>
<p><strong>Nr - Serie CI:</strong> {personalData?.inputSerie}
{personalData?.inputNumar}</p>
<p><strong>Email:</strong> {personalData?.inputEmail}</p>
</div>
</[Link]>

<[Link]>
<Button variant="outline-secondary" onClick={onClose}>
Închide
</Button>
</[Link]>
</Modal>

XX. useOptimistic
Def! useOptimistic este un hook din React folosit pentru actualizări optimiste
ale stării, adică atunci când vrei să actualizezi UI-ul imediat, înainte ca serverul să
confirme schimbarea. Este foarte util pentru UX rapid și fluid, mai ales în aplicații
cu interacțiuni sociale sau formulare.

Ce este „optimistic update”


Să zicem că ai un buton „Like” la un comentariu. Când utilizatorul apasă:
 Fără optimist: aștepți răspunsul serverului ca să actualizezi numărul de
like-uri.
 Cu optimist: UI-ul crește imediat numărul, iar dacă serverul confirmă,
rămâne; dacă apare o eroare, revii la starea inițială.

Sintaxa de bază
const [state, update] = useOptimistic(initialState, reducer);
 initialState – starea inițială.
 reducer – funcție (state, action) => newState care definește cum se
schimbă starea optimist.
 state – starea curentă (actualizată optimist)
 update – funcție pe care o apelezi cu acțiunea dorită pentru a aplica
schimbarea optimist.
Exemplu:
'use client';
import { useOptimistic } from 'react';
function Counter() {
const [count, updateCount] = useOptimistic(0, (state, delta) => state + delta);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => updateCount(1)}>Crește</button>
<button onClick={() => updateCount(-1)}>Scade</button>
</div>
);
}

Ce se întâmplă:

 Când dai click pe „Crește”, count se mărește imediat.


 Reducer-ul definește cum se aplică acțiunea (delta în exemplu).

Exemplu cu server:
function LikeButton({ postId }) {
const [likes, addLike] = useOptimistic(0, (state, action) => state + action);
async function handleLike() {
addLike(1); // update optimist
try {
await fetch(`/api/posts/${postId}/like`, { method: 'POST' });
} catch (err) {
addLike(-1); // rollback dacă serverul eșuează
}
}
return <button onClick={handleLike}>Like {likes}</button>;
}

 UI-ul se actualizează instant.


 Dacă serverul eșuează, facem rollback cu addLike(-1).

XXI. Router
Def! Un router în React se ocupă cu:
 Navigarea între pagini fără a reîncărca pagina.
 Maparea URL-urilor la componente React.
 Gestionarea istoricului (back/forward) și a stării navigației.
React Router v6+ are mai multe tipuri de routeri, dar cel mai folosit este
createBrowserRouter, care folosește URL-uri normale (nu hash-uri #).

Def! createBrowserRouter este o funcție din React Router v6.4+ care îți permite
să definești rutele aplicației tale folosind o structură de tip arbore și să folosești
un history bazat pe browser (adică URL-urile se schimbă normal în browser, fără
#). Este folosită pentru a crea un router modern cu suport pentru loader-e,
action-e și nested routes.

Definirea rutelor
Rutele se definesc ca un array de obiecte, fiecare obiect reprezentând o rută. Un
exemplu minimal:
import { createBrowserRouter } from "react-router-dom";
import Home from "./pages/Home";
import About from "./pages/About";
const router = createBrowserRouter([
{ path: "/", element: <Home /> },
{ path: "/about", element: <About /> },
]);

Props principale ale unei rute

Prop Ce face
path URL-ul pentru rută. Poate fi /, /about, /users/:id
element Componenta React care se renderizează pentru ruta respectivă
children Array cu rute copil (nested routes)
loader Funcție async care preia date înainte de renderizare
action Funcție async pentru a procesa formulare sau evenimente
errorElement Componenta care se afișează dacă apare eroare în loader/action
Definirea rutelor nested
import { createBrowserRouter } from "react-router-dom";
import MainLayout from "./MainLayout";
import Home from "./pages/Home";
import About from "./pages/About";
const router = createBrowserRouter([
{
path: "/",
element: <MainLayout />,
children: [
{ path: "/", element: <Home /> },
{ path: "about", element: <About /> },
],
},
]);
import { Outlet, Link } from "react-router-dom";

function MainLayout() {
return (
<div>
<header>
<Link to="/">Home</Link> | <Link to="/about">About</Link>
</header>
<hr />
<Outlet /> {/* Aici se va afișa Home sau About */}
<footer>© 2026 MyApp</footer>
</div>
);
}

export default MainLayout;

Dacă URL-ul este /, <Outlet /> afișează <Home />.


Dacă URL-ul este /about, <Outlet /> afișează <About />.

<Outlet /> este un placeholder în componenta ta React unde React Router va


reda componenta copil corespunzătoare rutei curente.

 Fără <Outlet />, rutele copil nu ar avea unde să fie afișate.


 Este folosit mai ales atunci când ai layout-uri comune pentru mai multe
rute.
Observații importante

1. <Outlet /> nu trebuie să fie singur – poți avea header, footer, sidebar, etc.,
în același layout.
2. Dacă o rută nu are copii, <Outlet /> nu afișează nimic.
3. Poți combina <Outlet /> cu useLoaderData() pentru a încărca date specifice
copilului.
4. <Outlet context={...} /> – poți transmite date de la layout la rutele copil
folosind useOutletContext().

useParams
useParams este un hook din React Router care îți permite să preiei parametrii
dinamici din URL.

Un parametru din URL este partea care se schimbă între rute, definită cu : în
path-ul rutei.

Exemplu:

import { createBrowserRouter } from "react-router-dom";


import User from "./pages/User";

const router = createBrowserRouter([


{
path: "/users/:id", // ":id" este parametrul din URL
element: <User />,
},
]);

În acest exemplu, :id este dinamic.


URL-uri posibile:

 /users/1 → id = "1"
 /users/42 → id = "42"
Utilizare

import { useParams } from "react-router-dom";

function User() {
const params = useParams(); // params este un obiect cu toți parametrii
[Link](params); // { id: "1" } dacă URL-ul este "/users/1"

return <div>User ID: {[Link]}</div>;


}

Observații:

 useParams returnează un obiect.


 Cheia obiectului este numele parametrului din path.
 Valoarea este valoarea curentă din URL.
 Numai parametrii definiți în path vor apărea în useParams.
 Pentru query string (?sort=asc) folosești useSearchParams.
 useParams funcționează doar în interiorul unui element de rută (nu în
afara Router-ului).

useLoaderData

Def! useLoaderData este un hook care permite componentelor să accesze


datele încărcate de loader pentru ruta curentă.
 loader este o funcție async definită în configurația rutei.
 Aceasta se execută înainte ca componenta să fie redată.
 useLoaderData returnează rezultatul returnat de loader.

Practic, este o modalitate oficială de a încărca date înainte de render fără a


folosi efecte (useEffect) manual.

import { createBrowserRouter } from "react-router-dom";


import User from "./pages/User";
const router = createBrowserRouter([
{
path: "/users/:id",
element: <User />,
loader: async ({ params }) => {
const res = await
fetch(`[Link]
if (![Link]) throw new Error("User not found");
return [Link]();
},
},
]);

Observații:

 loader primește un obiect cu:


o params → parametrii din URL (useParams).
o request → obiect similar cu fetch request (poate fi folosit pentru
formulare sau alte cereri).
 Loader-ul trebuie să returneze datele JSON (sau orice date necesare).

Utilizare useLoaderData

import { useLoaderData } from "react-router-dom";


function User() {
const user = useLoaderData(); // datele returnate de loader
return (
<div>
<h1>{[Link]}</h1>
<p>Email: {[Link]}</p>
</div>
);
}

Outlet
Def! <Outlet /> este un placeholder în componenta ta React unde React Router
va reda componenta copil corespunzătoare rutei curente.
 Fără <Outlet />, rutele copil nu ar avea unde să fie afișate.
 Este folosit mai ales atunci când ai layout-uri comune pentru mai multe
rute.

Exemplu
import { createBrowserRouter } from "react-router-dom";
import MainLayout from "./MainLayout";
import Home from "./pages/Home";
import About from "./pages/About";
const router = createBrowserRouter([
{
path: "/",
element: <MainLayout />,
children: [
{ path: "/", element: <Home /> },
{ path: "about", element: <About /> },
],
},
]);
import { Outlet, Link } from "react-router-dom";
function MainLayout() {
return (
<div>
<header>
<Link to="/">Home</Link> | <Link to="/about">About</Link>
</header>
<hr />
<Outlet /> {/* Aici se va afișa Home sau About */}
<footer>© 2026 MyApp</footer>
</div>
);
}
export default MainLayout;
Observații importante

1. <Outlet /> nu trebuie să fie singur – poți avea header, footer, sidebar, etc.,
în același layout.
2. Dacă o rută nu are copii, <Outlet /> nu afișează nimic.
3. Poți combina <Outlet /> cu useLoaderData() pentru a încărca date specifice
copilului.
4. <Outlet context={...} /> – poți transmite date de la layout la rutele copil
folosind useOutletContext().

XII. useRouter
Hook-ul useRouter este un instrument fundamental în [Link] folosit pentru a
accesa programatic routerul și pentru a gestiona navigarea în interiorul aplicației.
Funcționarea sa diferă major în funcție de arhitectura pe care o folosești: App
Router (introdus în [Link] 13+, directorul /app) sau Pages Router (arhitectura
clasică, directorul /pages).

1. App Router (next/navigation) – Recomandat în prezent


În noua arhitectură, hook-ul useRouter este importat din next/navigation și
rulează exclusiv în Client Components (componente care au directiva 'use client'
în vârf).
Funcții principale:
 [Link](href): Navighează programatic către o pagină nouă. Adaugă URL-ul
în istoricul browserului.
 [Link](href): Navighează către o pagină nouă, dar înlocuiește URL-ul
curent în istoric (utilizatorul nu se poate întoarce înapoi cu butonul "Back").
 [Link](): Reîmprospătează ruta curentă. Cere date noi de la server și re-
randează componentele, fără a pierde starea Client-side (de exemplu, textul
introdus în input-uri rămâne neschimbat).
 [Link](): Trimite utilizatorul la pagina anterioară din istoric.
 [Link](): Trimite utilizatorul la pagina următoare din istoric.
 [Link](href): Încarcă în fundal resursele pentru ruta specificată,
îmbunătățind instantaneu viteza de tranziție când utilizatorul va da click.
Exemplu:
'use client';
import { useRouter } from 'next/navigation';
export default function PaginaMea() {
const router = useRouter();
return (
<button onClick={() => [Link]('/dashboard')}>
Mergi la Dashboard
</button>
);
}
Note importante pentru App Router:
Spre deosebire de versiunile vechi, în App Router useRouter nu mai conține
informații despre URL-ul curent (cum ar fi query parametrii sau calea). Pentru
acestea, [Link] oferă hook-uri separate, specializate:
 usePathname(): Returnează calea curentă (ex: /produse/tricouri).
 useSearchParams(): Returnează parametrii de tip query (ex:
?marime=M&culoare=rosu).

2. Pages Router (next/router) – Arhitectura clasică


Dacă lucrezi într-un proiect mai vechi sau care folosește directorul /pages,
useRouter se importă din next/router. Aici, hook-ul este un "obiect gigant" care
conține atât metodele de navigare, cât și starea URL-ului.
Proprietăți și metode incluse:
 [Link]: Calea fișierului curent din folderul pages (ex: /produse/[id]).
 [Link]: URL-ul real afișat în browser (ex: /produse/123?promotie=true).
 [Link]: Un obiect ce conține parametrii dinamici și query (ex: { id: '123',
promotie: 'true' }).
 [Link]() / [Link](): Similare cu cele din App Router, dar acceptă și
trimiterea de obiecte de configurare.
 [Link]: Permite ascultarea schimbărilor de rută (ex: pornirea unei bare de
încărcare când se schimbă pagina)

Exemplu:
import { useRouter } from 'next/router';
export default function DetaliiProdus() {
const router = useRouter();
const { id } = [Link]; // Extrage ID-ul din URL dynamic route
return (
<div>
<h1>Produsul cu ID-ul: {id}</h1>
<button onClick={() => [Link]('/')}>Înapoi acasă</button>
</div>
);
}
Când să folosești useRouter vs <Link>?
 Folosește <Link> pentru orice navigare standard (meniuri, butoane simple, text
cu hiperlink). Este mai bun pentru SEO și face prefetch automat. [1]
 Folosește useRouter doar când navigarea depinde de o acțiune logică (ex: după
ce un formular s-a trimis cu succes, după validarea unei plăți sau la selectarea
unei opțiuni dintr-un dropdown).

XXII. useSearchParams

Def! useSearchParams din React Router sau useSearchParams din [Link]


App Router, ele au același scop general: citirea și modificarea parametrilor din
URL (query string), dar se folosesc puțin diferit.
În [Link] (App Router), searchParams nu este un React Hook propriu-zis, ci un
mecanism prin care poți accesa parametrii de tip query dintr-un URL (de
exemplu, ?page=2&search=pantofi).

Ce este searchParams?
 Este un obiect care conține cheile și valorile din URL-ul paginii, după semnul
întrebării (?).
 Permite implementarea de paginații, filtre, căutări și sortări dinamice.
 Spre deosebire de parametri de rută (/produse/[id]), parametrii query (?id=123)
sunt opționali și flexibili.
De ce există search params?
În loc să păstrezi anumite informații doar în state-ul aplicației, le poți pune în
URL.

Avantaje:
 utilizatorul poate da refresh fără să piardă filtrarea
 poate trimite link-ul altcuiva
 poate salva pagina la favorite
 browser-ul ține istoricul modificărilor

În React Router
import { useSearchParams } from "react-router-dom";
const [searchParams, setSearchParams] = useSearchParams();

1. searchParams
 Este un obiect de tip URLSearchParams (un API nativ al browserului) [].
 Reprezintă starea curentă a parametrilor din URL.
 Nu poți citi proprietățile direct ca dintr-un obiect normal ([Link] nu
va funcționa). Trebuie să folosești metode specifice [].

Cele mai folosite metode:


 [Link]("cheie") – returnează valoarea parametrului sau null [].
 [Link]("cheie") – verifică dacă parametrul există (returnează true /
false).

2. setSearchParams
 Este o funcție care îți permite să modifici URL-ul [].
 Funcționează exact ca funcția setStare dintr-un useState [].
 Când o apelezi, ea schimbă parametrii din URL și declanșează o rerandare a
componentei cu noile valori.

Exemplu:

1. Citirea parametrilor din URL:


import { useSearchParams } from 'react-router-dom';
function ListaProduse() {
const [searchParams] = useSearchParams();
const categorie = [Link]('categorie'); // Extrage ?categorie=valoare
return <div>Afișez produsele din: {categorie}</div>;
}

2. Modificarea parametrilor din URL:


import { useSearchParams } from 'react-router-dom';
function Filtru() {
const [searchParams, setSearchParams] = useSearchParams();
const schimbaFiltru = () => {
// Schimbă URL-ul în: ?categorie=electronice&sortare=pret
setSearchParams({ categorie: 'electronice', sortare: 'pret' });
};
return <button onClick={schimbaFiltru}>Filtrează</button>;
}

In NextJS
Cum se folosește în Server Components

În Server Components (componentele implicite din [Link]), searchParams este


primit direct ca un prop în componenta paginii ([Link]).
Important ([Link] 15+): Începând cu [Link] 15, searchParams este un Promise
și trebuie să folosești await pentru a-i citi valorile.

Exemplu:
// app/produse/[Link]
// Definim tipul dacă folosești TypeScript (opțional)
type SearchParams = Promise<{ [key: string]: string | string[] | undefined }>;
export default async function Page({ searchParams }: { searchParams:
SearchParams }) {
// Așteptăm rezolvarea promise-ului pentru a citi parametrii
const params = await searchParams;
const queryCautare = [Link] || '';
const paginaCurenta = [Link] || '1';
return (
<div>
<h1>Rezultatele căutării pentru: {queryCautare}</h1>
<p>Te afli la pagina: {paginaCurenta}</p>
</div>
);
}

Cum se folosește în Client Components

Dacă o componentă are directiva "use client", nu poți primi searchParams ca prop
direct. În schimb, [Link] îți pune la dispoziție un React Hook real numit
useSearchParams().
Acest hook returnează o instanță de tip URLSearchParams, ceea ce înseamnă că
citești valorile folosind metoda .get().
Exemplu:
"use client";
import { useSearchParams } from "next/navigation";
export default function FiltreProduse() {
const searchParams = useSearchParams();
// Citim parametrii din URL (?categorie=electronice)
const categorie = [Link]("categorie");
return (
<div>
<p>Categoria selectată: {categorie || "Toate"}</p>
</div>
);
}

Cum modifici parametrii URL în mod dinamic


Pentru a actualiza parametrii din URL de pe client (fără a reîncărca complet
pagina), combini useSearchParams, usePathname și useRouter.
Exemplu:
"use client";
import { useSearchParams, usePathname, useRouter } from "next/navigation";
export default function BaraCautare() {
const searchParams = useSearchParams();
const pathname = usePathname();
const { replace } = useRouter();
function handleSearch(termen: string) {

// 1. Copiem parametrii existenți într-un obiect mutabil


const params = new URLSearchParams(searchParams);

// 2. Adăugăm sau ștergem parametrul în funcție de input


if (termen) {
[Link]("search", termen);
} else {
[Link]("search");
}

// 3. Actualizăm URL-ul în browser


replace(`${pathname}?${[Link]()}`);
}

return (
<input
type="text"
placeholder="Caută produse..."
defaultValue={[Link]("search")?.toString()}
onChange={(e) => handleSearch([Link])}
/>
);
}

 În [Link] (App Router), orice componentă Client care utilizează


useSearchParams trebuie învelită într-un bloc <Suspense>.
 Dacă nu ai face asta, [Link] ar dezactiva randarea statică pe server (SSR) pentru
întreaga pagină în timpul build-ului, deoarece parametrii din URL sunt cunoscuți
doar în momentul în care utilizatorul accesează efectiv pagina în browser.
Diferentele fundamentale: React Router vs NextJS:
React Router [Link] App Router
Citește query params Citește query params
Modifică prin [Link]() /
Modifică prin setSearchParams()
[Link]()
Returnează [searchParams,
Returnează doar searchParams
setSearchParams]
Hook-ul gestionează și actualizarea
Hook-ul este doar pentru citire
URL-ului
Poate fi folosit în componente React Poate fi folosit doar în Client Components
Router ("use client")

XXII. React Query

Tanstack Query

Definitie! TanStack Query (fostă React Query) este o librărie pentru gestionarea
datelor asincrone în aplicații frontend — în special pentru React, dar există și
variante pentru Vue, Solid, Svelte etc.

Fara tanstack
const [data, setData] = useState(null)
const [loading, setLoading] = useState(true)
const [error, setError] = useState(null)
useEffect(() => {
fetch('/api/users')
.then(r => [Link]())
.then(setData)
.catch(setError)
.finally(() => setLoading(false))
}, [])
TanStack Query are câteva concepte fundamentale:

1. Query Client
2. Queries
3. Query Keys
4. Query Functions
5. Cache
6. Stale vs Fresh
7. Mutations
8. Invalidation
9. Refetching
10. Pagination / Infinite Queries

1. Query Client
 Este “creierul” aplicației.
 gestioneaza: cache-ul, queries, mutations, refetching, sincronizarea
 setup: npm install @tanstack/react-query
 configurare:
import {
QueryClient,
QueryClientProvider,
} from "@tanstack/react-query"

const queryClient = new QueryClient()


function App() {
return (
<QueryClientProvider client={queryClient}>
<Root />
</QueryClientProvider>
)
}

2. Query
 este o operatie de citirea a datelor;
 exemple: get /users, get /posts, get /profile;
 in general:
o queries = read;
o mutations = write;

3. useQuery
 hook-ul principal;
 exemplu:
import { useQuery } from "@tanstack/react-query"
function Users() {
const { data, isLoading, error} = useQuery({
queryKey: ["users"],
queryFn: fetchUsers,
});
if (isLoading) return <div>Loading...</div>
if (error) return <div>Error</div>
return (
<ul>
{[Link](user => (
<li key={[Link]}>{[Link]}</li>
))}
</ul>
)
}
4. queryKey
 identifica unic datele din cache;
 ex: [“users”], [“users”, userId], [“posts”, {page: 1, filter: “react”}]
 se foloseste pentru: cache, refetch, invalidation, deduplicare;

5. queryFn
 functia care face request;
const fetchUsers = async () => {
const res = await fetch("/api/users")
if (![Link]) {throw new Error("Failed")}
return [Link]()
}
 apoi
useQuery({
queryKey: ["users"],
queryFn: fetchUsers,
})

6. Cache
 tanstack cache-uieste automat datele;
 avantaje:
o nu reface request-uri inutile,
o date instant,
o UX mai rapid,
o sincronizare între componente.

7. Stale vs Fresh

În TanStack Query, fiecare query are un „status de prospețime”:


 Fresh = date considerate încă valide
 Stale = date considerate posibil expirate

Cum functioneaza?
useQuery({
queryKey: ['users'],
queryFn: fetchUsers,
staleTime: 5000
})

 tip de 5 secunde datele sunt fresh;


 dupa 5 secunde datele devin stale;
 TanStack NU șterge datele automat, doar spune: “Pot fi vechi. Dacă apare
un trigger, refacem request-ul.”
 trigger-ele tipice:
o user revine pe tab
o componenta se remontează
o reconnect la internet
o refetch manual

const users = useQuery({


queryKey: ['users'],
queryFn: fetchUsers,
staleTime: 60000
})
 timp de 1 minut nu se mai face request
 toate componentele folosesc cache-ul
 UX-ul e foarte rapid
 dupa 1 min:
o datele sunt stale;
o UI-ul încă afișează cache-ul
o eventual se face background refetch

8. Mutations

Queries = citire date


Mutations = modificare date
Folosești mutations pentru: create, update, delete, login, upload, forms

Exemplu:
const mutation = useMutation({
mutationFn: createUser
})

Trigger:
[Link]({
name: 'Alex'
})
State-urile mutation
[Link]
[Link]
[Link]
[Link]
[Link]

Obs: Mutations nu folosesc cache ca queries

9. Invalidation
După o mutation, cache-ul poate deveni incorect.

invalidateQueries
const queryClient = useQueryClient()
[Link]({
queryKey: ['users']
})

 marchează query-ul ca stale


 poate declanșa refetch automat

Flux tipic:
const queryClient = useQueryClient()

const mutation = useMutation({


mutationFn: createUser,
onSuccess: () => {
[Link]({
queryKey: ['users']
})
}
})

invalidate vs refetch
invalidateQueries
 marchează stale
 eventual refetch
refetchQueries
 face request imediat
În practică:
 invalidate e preferat mai des

10. Pagination / Infinite Queries


const { data } = useQuery({
queryKey: ['users', page],
queryFn: () => fetchUsers(page)
})
 fiecare pagină are cache separat
keepPreviousData
useQuery({
queryKey: ['users', page],
queryFn: () => fetchUsers(page),
placeholderData: keepPreviousData
})
 nu mai apare flicker/loading între pagini

Iniginite Queries
const {data, fetchNextPage, hasNextPage, isFetchingNextPage} =
useInfiniteQuery({
queryKey: ['posts'],
queryFn: ({ pageParam = 1 }) =>
fetchPosts(pageParam),
getNextPageParam: (lastPage) => [Link]})

QueryClient
QueryClient este „motorul” care gestionează:
 cache-ul pentru request-uri
 refetch automat
 invalidarea datelor
 retry logic
 sincronizarea între componente
 stările loading/error/success
Practic, el ține în memorie toate query-urile aplicației.

Exemplu:
import { QueryClient } from '@tanstack/react-query'
const queryClient = new QueryClient()

 Aici creezi o instanță globală care va controla toate query-urile.


 Poți să-l vezi ca pe: „managerul central pentru datele venite de la server”
 Intern, QueryClient are: QueryCache, MuatationCache, observers, retry
handlers, stale timers, garbage collection pentru cache
QueryClientProvider

QueryClientProvider este componenta care pune acel QueryClient la dispoziția


întregii aplicații React prin Context API.

Exemplu:
import React from 'react'
import ReactDOM from 'react-dom/client'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import App from './App'
const queryClient = new QueryClient()
[Link]([Link]('root')!).render(
<QueryClientProvider client={queryClient}>
<App />
</QueryClientProvider>
)

Fluxul este:
QueryClient

QueryClientProvider

toate componentele React

useQuery / useMutation

Cand faci:
useQuery({
queryKey: ['users'],
queryFn: fetchUsers
})
hook-ul caută QueryClient din provider și:
 verifică dacă datele există în cache
 decide dacă trebuie refetch
 salvează răspunsul
 notifică toate componentele care folosesc acea query
Fără React Query:
const [data, setData] = useState()
const [loading, setLoading] = useState()
useEffect(() => {
fetch('/api/users')
.then(r => [Link]())
.then(setData)
}, [])
 trebuie gestionate manual: loading, errors, cache, refetch, retry,
sincronizare;

Cu React Query:
const { data, isLoading, error } = useQuery({
queryKey: ['users'],
queryFn: fetchUsers
})
 totul devine automat;

createRoutesFromElements
createRoutesFromElements este o funcție care transformă JSX-ul rutei într-un
array de obiecte de rute.

 În loc să scrii rutele ca array de obiecte ([{ path, element, children }]), le
poți scrie ca JSX, ceea ce poate fi mai intuitiv.
 Este folosit împreună cu createBrowserRouter sau alte tipuri de routere.

Exemplu:
import { createBrowserRouter, createRoutesFromElements, Route } from "react-
router-dom";
import MainLayout from "./MainLayout";
import Home from "./pages/Home";
import About from "./pages/About";
const router = createBrowserRouter(
createRoutesFromElements(
<Route path="/" element={<MainLayout />}>
<Route index element={<Home />} /> {/* index = ruta implicită */}
<Route path="about" element={<About />} />
</Route>
)
);
 <Route index element={...} /> = ruta implicită pentru layout-ul părinte (/).
 <Route path="about" /> = ruta /about sub layout-ul /.

Link
<Link> este componenta din React Router care înlocuiește elementul HTML <a>
pentru navigarea internă între rute.
 Navigarea se face fără reîncărcarea paginii (SPA – Single Page
Application).
 Actualizează URL-ul și redă componenta corespunzătoare automat,
folosind router-ul.

Exemplu

import { Link } from "react-router-dom";


function Navbar() {
return (
<nav>
<Link to="/">Home</Link>
<Link to="/about">About</Link>
<Link to="/contact">Contact</Link>
</nav>
);
}
 to = URL-ul rutei unde vrem să navigăm
 Nu provoacă refresh de pagină, spre deosebire de <a href="/about">.

Link absolut
<Link to="/about">About</Link>
 Merge către /about din rădăcina site-ului.

Link relativ
<Link to="settings">Settings</Link>
 Fără / la început, URL-ul este relativ la ruta curentă.
Fragment
În React, Fragment este un mecanism care îți permite să grupezi mai multe
elemente JSX fără să adaugi un element suplimentar în DOM (cum ar fi un <div>
inutil).

Fara Fragment:
function Exemplu() {
return (
<div>
<h1>Titlu</h1>
<p>Descriere</p>
</div>
);
}

Problema:

 acel <div> ajunge în DOM


 uneori strică layout-ul sau CSS-ul
 creează noduri inutile

Cu Fragment:
import { Fragment } from 'react';
function Exemplu() {
return (
<Fragment>
<h1>Titlu</h1>
<p>Descriere</p>
</Fragment>
);
}
React grupează elementele logic, dar nu generează un element HTML în plus.

Cand e util Fragment:


 in tabele;
 pentru DOM mai curat;
 Layout flex/grid;

Diferența dintre Fragment și <div>


Caracteristică Fragment div
Apare în DOM ❌ ✅
Poate avea stil CSS ❌ ✅
Poate avea event listeners ❌ ✅
Grupează JSX ✅ ✅
Sintaxa scurtă <> </> NU acceptă key.
Dacă ai nevoie de key într-un .map(), folosești forma completă:
import { Fragment } from 'react';

[Link](item => (
<Fragment key={[Link]}>
<h2>{[Link]}</h2>
<p>{[Link]}</p>
</Fragment>
))

getStaticProps

Idei generale:
 rulează la build time;
 generează pagini statice (HTML deja pregătit);
 foarte rapid;
 bun pentru conținut care nu se schimbă des;

Sintaxa de baza:
export async function getStaticProps() {
// fetch data from an API
return {
props: {
meetups: DUMMY_MEETUPS
},
revalidate: 1
};
}

Ce face functia?
 rulează pe server;
 ia datele;
 creează HTML-ul paginii;
 [Link] salvează pagina ca fișier static;

Flow-ul complet
 Pasul 1 – Build: npm run build
 Pasul 2 – Datele sunt obtinute: meetups: DUMMY_MEETUPS
const response = await fetch('[Link]
const data = await [Link]();
 Pasul 3 – Se genereaza HTML static:
<h1>Lista Meetups</h1>
<ul>
...
</ul>

Ce inseamna props?
return {
props: {
meetups: DUMMY_MEETUPS
}
}

Datele ajung in componenta ca props:


function HomePage(props) {
[Link]([Link]);
}
sau
function HomePage({ meetups }) {}

Ce face revalidate?
revalidate: 1 -> activeaza ISR = Incremental Static Regeneration
 pagina este statica dar poate fi regenerata automat dupa minim 1
sec
User 1 -> primește pagina statică
după 1 secundă:
User 2 -> [Link] regenerează pagina în background
Cand se foloseste getStaticProps?
- blog
- produse
- landing pages
- documentație
- articole
- conținut public

getServerSideProps

Idei generale:
 rulează la fiecare request
 generează pagina pe server în timp real
 mai flexibil
 bun pentru date dinamice

Sintaxa de baza:
export async function getServerSideProps(context) {
const req = [Link];
const res = [Link];
// fetch data from an API
return {
props: {
meetups: DUMMY_MEETUPS
},
};
}

Ce face functia?
La fiecare request:
-> user intră pe pagină
-> [Link] rulează funcția
-> ia datele
-> generează HTML
-> trimite răspunsul

Ce este context?
context conține informații despre request.
const req = [Link]; -> este request-ul HTTP
const res = [Link]; -> este response-ul HTTP

Cand se foloseste getServerSideProps?


 dashboard;
 user profile
 autentificare

Suspense
Definitie! Suspense în React este o componentă care te ajută să gestionezi stările
de „așteptare” (loading) în aplicații, mai ales când încarci date sau componente
care nu sunt disponibile imediat.

Cum functioneaza?

Suspense înconjoară o componentă și îi spune React-ului:


„Dacă această parte nu e gata încă, arată un fallback (de ex. loader) până se
încarcă.”
import { Suspense } from "react";

<Suspense fallback={<div>Se încarcă...</div>}>


<MyComponent />
</Suspense>

- fallback = ce afișezi cât timp „aștepți”


- MyComponent = componenta care poate întârzia (loading)

Unde se foloseste?
1. Lazy loading de componente
import { lazy, Suspense } from "react";
const Dashboard = lazy(() => import("./Dashboard"));

function App() {
return (
<Suspense fallback={<div>Se încarcă dashboard-ul...</div>}>
<Dashboard />
</Suspense>
);
}
Aici Dashboard se încarcă doar când e nevoie (code splitting).

2. Data fetching (cu framework-uri moderne)

În React modern (mai ales cu frameworks ca [Link] sau React Server


Components), Suspense poate aștepta date:
<Suspense fallback={<Spinner />}>
<UserProfile />
</Suspense>

Important
 Nu funcționează „automat” cu orice async (fetch clasic în useEffect NU
declanșează Suspense).
 Este gândit pentru:
o [Link]
o framework-uri care suportă Suspense data fetching
 Este parte din arhitectura modernă React (Concurrent Features)

XXIII. RSC (React Server Components)


Definitie! React Server Components (RSC) reprezintă o arhitectură și un set de
API-uri introdus în React (din versiunea 19), prin care componentele sunt randate
pe server înainte de a fi trimise către client, într-un mediu separat de aplicația
client sau de SSR.
React Router oferă integrare cu RSC prin API-uri compatibile cu bundlerele care
suportă această tehnologie, permițând utilizarea Server Components și Server
Functions în aplicații.
Pentru utilizare corectă, este recomandată familiarizarea cu documentația
oficială React despre Server Components. Suportul RSC este disponibil atât în
Framework Mode, cât și în Data Mode, însă API-urile și comportamentul diferă
între aceste moduri.
Cel mai rapid mod de a începe este folosirea unor template-uri React Router deja
configurate pentru RSC, care includ funcționalități precum SSR, Server
Components, Client Components („use client”) și Server Functions („use server”).

Există două variante principale de template-uri:


 RSC Framework Mode, bazat pe pluginul experimental React Router RSC
pentru Vite
 RSC Data Mode, bazat pe pluginul experimental Vite RSC
Framework Mode este similar cu varianta non-RSC, diferențele fiind în principal
la nivel de API-uri și comportamente specifice RSC, pe care documentația le
detaliază.

În RSC Framework Mode, React Router folosește un plugin Vite diferit față de
modul clasic, numit unstable_reactRouterRSC, care trebuie folosit împreună cu
pluginul experimental @vitejs/plugin-rsc. Ordinea contează: pluginul React
Router trebuie să fie înaintea celui RSC.

import { defineConfig } from "vite";


import { unstable_reactRouterRSC as reactRouterRSC } from "@react-
router/dev/vite";
import rsc from "@vitejs/plugin-rsc";
export default defineConfig({
plugins: [reactRouterRSC(), rsc()],
});

Build server (output)

În acest mod, build-ul server (build/server/[Link]) exportă implicit un handler de


tip: (request: Request) => Promise<Response>. Acesta poate fi folosit direct sau
adaptat pentru [Link] (de exemplu cu Express), prin createRequestListener din
@remix-run/node-fetch-server.

import express from "express";


import requestHandler from "./build/server/[Link]";
import { createRequestListener } from "@remix-run/node-fetch-server";

const app = express();


[Link]("/assets", [Link]("build/client/assets", {
immutable: true,
maxAge: "1y",
}));
[Link]([Link]("build/client"));
[Link](createRequestListener(requestHandler));
[Link](3000);

React elements din loaders/actions

În RSC mode, loader și action pot returna date + elemente React, dar aceste
elemente sunt randate doar pe server.
export async function loader() {
return {
message: "Message from the server!",
element: <p>Element from the server!</p>,
};
}
export default function Route({ loaderData }) {
return (
<>
<h1>{[Link]}</h1>
{[Link]}
</>
);
}

Componente client în elemente server


Dacă ai nevoie de interactivitate (hooks, evenimente), componenta trebuie
marcată ca client module ("use client"), apoi importată în loader.

"use client";
import { useState } from "react";

export function Counter() {


const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(count + 1)}>
Count: {count}
</button>
);
}
import { Counter } from "./counter";

export async function loader() {


return {
message: "Message from the server!",
element: (
<>
<p>Element from the server!</p>
<Counter />
</>
),
};
}

În React Router RSC Framework Mode, o rută poate fi definită ca Server


Component în loc de componenta clasică default. Dacă folosești
ServerComponent, ruta se execută pe server, iar randarea client-side este
eliminată. Nu poți exporta simultan default și ServerComponent din același
modul.

1. ServerComponent vs Client default


import type { Route } from "./+types/route";
import { Outlet } from "react-router";
import { getMessage } from "./message";
export async function loader() {
return {
message: await getMessage(),
};
}
export function ServerComponent({ loaderData }: [Link]) {
return (
<>
<h1>Server Component Route</h1>
<p>Message from the server: {[Link]}</p>
<Outlet />
</>
);
}

Ideea:
 ServerComponent = randare pe server
 loaderData vine direct din server
 Outlet funcționează normal pentru nested routes
 nu există componentă default

2. Maparea componentelor server vs client


RSC introduce perechi echivalente:
 ServerComponent ↔ default
 ServerErrorBoundary ↔ ErrorBoundary
 ServerLayout ↔ Layout
 ServerHydrateFallback ↔ HydrateFallback

Ideea:
 server și client au echivalente separate
 nu se amestecă în același export
3. Componente client în interiorul unui ServerComponent
Dacă ai nevoie de hook-uri sau evenimente, trebuie să muți acea logică într-un
modul client ("use client").
"use client";
import { useState } from "react";
export function Counter() {
const [count, setCount] = useState(0);

return (
<button onClick={() => setCount(count + 1)}>
Count: {count}
</button>
);
}

import { Counter } from "./counter";

export function ServerComponent() {


return (
<>
<h1>Counter</h1>
<Counter />
</>
);
}

Ideea:
 ServerComponent rămâne server-only
 interactivitatea se izolează în componente client

4. Înlocuirea .server / .client modules


În RSC mode:
 NU mai există suport nativ pentru fișiere .server / .client
 se folosesc importuri speciale:
o "server-only"
o "client-only"

Ideea:
 import "server-only" blochează includerea în client build
 validare se face la build-time prin @vitejs/plugin-rsc
5. Migrare de la convențiile vechi .server/.client
Se poate folosi vite-env-only pentru a preveni importurile greșite:
import { defineConfig } from "vite";
import { denyImports } from "vite-env-only";
import { unstable_reactRouterRSC as reactRouterRSC } from "@react-
router/dev/vite";
import rsc from "@vitejs/plugin-rsc";

export default defineConfig({


plugins: [
denyImports({
client: { files: ["**/.server/*", "**/*.server.*"] },
}),
reactRouterRSC(),
rsc(),
],
});
Ideea generală
 RSC introduce rute server-first
 fiecare componentă are echivalent server/client strict separat
 nu se mai amestecă logică de client în server fără izolare
 separarea se face prin:
o ServerComponent
o "use client"
o "server-only" / "client-only"
 vechile convenții .server/.client sunt înlocuite cu mecanisme build-time mai
sigure

În RSC Framework Mode, există suport pentru MDX routes, dar cu o limitare
importantă: componentele exportate din MDX trebuie să fie compatibile cu RSC
(adică fără Hooks sau alte feature-uri client-only). Dacă este nevoie de
interactivitate, logica trebuie mutată într-un modul "use client" separat.

1. MDX Routes (restricții RSC)


 MDX este suportat prin @mdx-js/rollup v3.1.1+
 componentele din MDX:
o ✔ pot fi server components
o ❌ nu pot folosi Hooks (ex: useState, useEffect)
 pentru interactivitate → se extrag în client modules

Ideea:
MDX în RSC este server-first, nu „React complet interactiv”.

2. Custom Entry Files


RSC permite personalizarea celor 3 puncte de intrare:
 app/[Link](x) → server RSC entry
 app/[Link](x) → SSR entry (HTML generation)
 app/[Link] → client hydration entry
Dacă nu sunt definite, framework-ul folosește implementările implicite.

3. Override simplu – RSC entry


Poți înfășura comportamentul default și adăuga logică (ex: logging):

import defaultEntry from "@react-router/dev/config/default-rsc-entries/[Link]";


import { RouterContextProvider } from "react-router";
export default {
fetch(request: Request): Promise<Response> {
[Link]("Custom RSC entry handling request:", [Link]);
const requestContext = new RouterContextProvider();
return [Link](request, requestContext);
},
};
if ([Link]) {
[Link]();
}

Ideea:
 RSC entry controlează request handling server-side
 poți intercepta request-uri înainte de routing

4. Override SSR entry


Pentru generarea HTML-ului:
import { generateHTML as defaultGenerateHTML } from "@react-
router/dev/config/default-rsc-entries/[Link]";
export function generateHTML(request, serverResponse) {
[Link]("Custom SSR entry generating HTML for:", [Link]);
return defaultGenerateHTML(request, serverResponse);
}

Ideea:
 SSR entry controlează HTML output-ul final
 poate fi extins, nu înlocuit complet

5. Client entry
Client entry poate rămâne default sau poate fi importat direct:
import "@react-router/dev/config/default-rsc-entries/[Link]";

Ideea:
 client entry gestionează hydration-ul
 de obicei nu necesită modificări

6. Customizare avansată
Pentru modificări profunde:
 copiezi fișierele default din:
o [Link]
o [Link]
o [Link]
 le modifici local
Acestea se găsesc în:
 node_modules/@react-router/dev/dist/config/default-rsc-entries/
Obligatoriu păstrezi:
 [Link] → export default { fetch }
 [Link] → generateHTML
 [Link] → hydration logic

7. Comenzi utile
Poți inspecta implementările implicite cu:
 react-router reveal [Link]
 react-router reveal [Link]
 react-router reveal [Link]

8. Config opțiuni neacceptate în RSC


În RSC mode NU sunt suportate:
 buildEnd
 presets
 serverBundles
 future.v8_splitRouteModules
 subResourceIntegrity

Ideea generală
 MDX este permis, dar strict server-safe
 aplicația are 3 entry points clare: RSC, SSR, client
 fiecare poate fi extins sau înlocuit controlat
 RSC Mode elimină multe configurări vechi pentru a simplifica pipeline-ul și
a impune separarea server/client

RSC Data Mode

RSC Data Mode este stratul de bază peste care este construit RSC Framework
Mode. Oferă mai multă flexibilitate, dar mai puține facilități out-of-the-box (fără
file-system routing, HMR sau hot data revalidation). Este util mai ales când vrei
integrare cu un bundler sau server personalizat.
1. Definirea rutelor (manual, fără file-based routing)
Rutele sunt definite explicit prin matchRSCServerRequest, folosind o structură de
config.

function Root() {
return <h1>Hello world</h1>;
}
matchRSCServerRequest({
routes: [{ path: "/", Component: Root }],
});
Ideea:
 rutele nu mai vin din fișiere
 sunt definite programatic
 control complet asupra routing-ului
2. Recomandare: lazy route modules
Pentru performanță și organizare, se recomandă lazy() imports, care reiau
modelul de Route Modules din Framework Mode.

import type { unstable_RSCRouteConfig as RSCRouteConfig } from "react-router";


export function routes() {
return [
{
id: "root",
path: "",
lazy: () => import("./root/route"),
children: [
{
id: "home",
index: true,
lazy: () => import("./home/route"),
},
{
id: "about",
path: "about",
lazy: () => import("./about/route"),
},
],
},
] satisfies RSCRouteConfig;
}

Ideea:
 aceleași route module exports ca în Framework Mode:
o loader, action, meta, headers
o ErrorBoundary, HydrateFallback
o client/server annotations
 diferența: totul e manual orchestrat
3. Server Components ca default route export
În Data Mode, fiecare rută exportă implicit un Server Component.
export default function Home() {
return (
<main>
<article>
<h1>Welcome to React Router RSC</h1>
<p>You won't find me running any JavaScript in the browser!</p>
</article>
</main>
);
}

Ideea:
 totul rulează pe server by default
 client JS nu este necesar

4. Server Components async (data fetching direct în componentă)


Un avantaj major: poți face await direct în componentă.

export default async function Home() {


let user = await getUserData();

return (
<main>
<article>
<h1>Welcome to React Router RSC</h1>
<p>You won't find me running any JavaScript in the browser!</p>
<p>Hello, {user ? [Link] : "anonymous person"}!</p>
</article>
</main>
);
}

Ideea:
 elimină nevoia de loader în multe cazuri
 componenta devine direct strat de data-fetching + UI

5. Loaders în RSC Data Mode


Loaders și actions rămân disponibile, dar rolul lor se schimbă:
 utile pentru:
o status codes
o redirects
 mai puțin pentru data fetching (înlocuit de Server Components)
Ideea:
 data fetching se mută în componentă
 loaders devin „control layer”, nu „data layer”

6. Server Functions (React feature)


Server Functions sunt funcții marcate cu "use server" care rulează exclusiv pe
server și pot fi apelate din UI.

Definire:
"use server";
export async function updateFavorite(formData: FormData) {
let movieId = [Link]("id");
let intent = [Link]("intent");
if (intent === "add") {
await addFavorite(Number(movieId));
} else {
await removeFavorite(Number(movieId));
}
}
Utilizarea intr-un form:
import { updateFavorite } from "./[Link]";

export async function AddToFavoritesForm({ movieId }: { movieId: number }) {


let isFav = await isFavorite(movieId);

return (
<form action={updateFavorite}>
<input type="hidden" name="id" value={movieId} />
<input type="hidden" name="intent" value={isFav ? "remove" : "add"} />
<AddToFavoritesButton isFav={isFav} />
</form>
);
}

Ideea:
 funcțiile rulează pe server automat
 pot fi apelate direct din forms
 React Router revalidează automat UI după execuție

7. Auto revalidare
După apelul unui Server Function:
 React Router face automat:
o re-fetch / re-render server UI
o update UI fără cache invalidation manual
Ideea:
 nu gestionezi manual cache-ul
 sistemul menține consistența server-client automat

Client Properties + Bundler/Entry Configuration în RSC

În RSC, rutele sunt definite pe server, dar pot expune logică de tip client prin
mecanisme speciale (clientLoader, clientAction, shouldRevalidate) folosind "use
client" și module separate. Acest lucru permite un mix controlat între server
routing și comportament client-side.

1. Client Properties (clientLoader / clientAction / shouldRevalidate)


Aceste funcții sunt definite într-un modul client și marchează comportamente
care rulează în browser.
"use client";

export function clientAction() {}


export function clientLoader() {}

export function shouldRevalidate() {}


export default function ClientRoot() {
return <p>Client route</p>;
}

Ideea:
 clientLoader → fetch pe client
 clientAction → mutații pe client
 shouldRevalidate → control re-fetch/re-render
 totul rulează în context "use client"

2. Re-export în route module (lazy route)


Aceste funcții sunt apoi expuse din route module-ul server-side:

export {
clientAction,
clientLoader,
shouldRevalidate,
} from "./client";

export default function Root() {


// ...}

Ideea:
 route-ul server devine „wrapper”
 logica client este importată separat
 păstrează separarea server/client, dar le leagă logic

3. Transformarea unui route complet în Client Component


Un route poate fi complet client-side prin delegare către un modul .client.

import { default as ClientRoot } from "./[Link]";

export {
clientAction,
clientLoader,
shouldRevalidate,
} from "./client";
export default function Root() {
// necesar ca bundlerul să păstreze un server boundary
return <ClientRoot />;
}

Ideea:
 ruta rămâne compatibilă cu RSC pipeline
 UI real este complet client-rendered
 server component există doar ca „boundary”

4. Arhitectura bundler + entry points


RSC necesită integrare cu un bundler și 3 entry points principale:
a) SSR server entry ([Link])
Responsabil pentru:
 primirea request-ului
 conversia RSC → HTML
 SSR / SSG / ISR
API-uri:
 routeRSCServerRequest
 RSCStaticRouter
b) RSC server entry ([Link])
Responsabil pentru:
 matching request → route
 generare RSC payload
API-uri:
 matchRSCServerRequest

c) Client entry ([Link])


Responsabil pentru:
 hydration HTML
 activare Server Actions post-hydration
API-uri:
 createCallServer
 getRSCStream
 RSCHydratedRouter
5. Ideea de arhitectură (important)
 Nu există neapărat 2 servere separate
 există 2 „module graphs” în același server:
o RSC graph (server components)
o SSR graph (HTML rendering)
Flux simplificat:
1. request → [Link] → RSC payload
2. RSC payload → [Link] → HTML
3. browser → [Link] → hydration + server actions

6. Ideea generală
 rutele sunt server-defined, dar pot expune logică client
 client logic este izolat în "use client" modules
 route modules doar re-exportă comportamente client
 aplicația are 3 entry points clare:
o RSC server
o SSR server
o browser hydration
 totul este orchestrat de bundler, nu de framework magic

XXIV. React Behind the Scene


React afișează componentele pe ecran printr-un proces în 4 etape principale:

1. Render Triggered – procesul începe atunci când se modifică starea (state)


sau proprietățile (props).
2. Render Phase – React execută funcțiile componentelor și calculează ce
modificări sunt necesare în DOM.
3. Commit Phase – React aplică efectiv modificările în DOM (adaugă,
actualizează sau șterge elemente).
4. Browser Paint – browserul desenează rezultatul final pe ecran.

Un aspect esențial: renderizarea în React NU înseamnă actualizarea DOM-ului


și nici afișarea pe ecran. Renderizarea este doar un proces intern prin care React
determină cum ar trebui să arate interfața.
Diagrama generala
┌─────────────────┐
│ State/Props se modifică
└────────┬────────┘


┌─────────────────┐
│ 1. Render Triggered
└────────┬────────┘


┌─────────────────┐
│ 2. Render Phase │
│ - React apelează componentele
│ - Compară noul Virtual DOM │
└────────┬────────┘


┌─────────────────┐
│ 3. Commit Phase │
│ - Actualizează │
│ DOM-ul real │
└────────┬────────┘


┌─────────────────┐
│ 4. Browser Paint│
│ - Browserul │
│ afișează UI │
Cum funcționează în spatele scenei
State nou


React creează un nou Virtual DOM


Compară cu Virtual DOM anterior
(Diffing Algorithm)


Identifică modificările minime


Actualizează DOM-ul real


Browserul redesenează pagina

Diferența dintre Render și Commit

RENDER
────────────────────────
✔ Rulează componentele
✔ Calculează schimbările
✔ Lucrează în memorie
✖ Nu modifică DOM-ul

COMMIT
────────────────────────
✔ Aplică schimbările
✔ Modifică DOM-ul real
✔ Pregătește afișarea

Exemplu

function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(count + 1)}>
{count}
</button>
);
}
Click


setCount()


Render Triggered


React rulează Counter()


Observă că valoarea s-a schimbat


Commit Phase


DOM actualizat


Browser Paint


Utilizatorul vede noua valoare

Render Triggered
Procesul de renderizare React este declanșat în două situații:
1. La încărcarea inițială a aplicației (Initial Render).
2. Când se modifică state-ul sau props-urile unei componente.
Important: React nu execută render-ul instantaneu. Actualizările sunt
planificate (scheduled) și grupate (batching) pentru a evita re-renderizările inutile
și pentru a optimiza performanța.

Ce se întâmplă după declanșare?


React creează un nou Virtual DOM Tree, iar componentele afectate sunt
executate din nou pentru a genera o nouă reprezentare a interfeței.\
Diagrama
State Update / Initial Load


Render Triggered


React planifică lucrul (Scheduling)


Creează Virtual DOM Nou


Execută componentele

Render Phase
În această etapă React calculează cum ar trebui să arate interfața după
modificări.

Virtual DOM
Virtual DOM este o copie ușoară a DOM-ului real păstrată în memorie.
Când state-ul se schimbă:

1. React execută funcția componentei.


2. Creează un nou Virtual DOM.
3. Compară noul Virtual DOM cu cel anterior.
4. Determină modificările necesare.

Diagrama
Real DOM

│ sincronizare

Virtual DOM (vechi)


State schimbat


Virtual DOM (nou)
Re-renderizarea componentelor
Când o componentă părinte se re-renderizează:
 React execută din nou și componentele copil.
 Nu înseamnă că DOM-ul lor va fi actualizat.
 React verifică ulterior dacă există modificări reale.

Fiber Reconciler

În React modern, reconciliation este realizată de React Fiber.


Fiber este o structură internă care păstrează informații despre fiecare
componentă și element DOM.

Fiber NU este recreat la fiecare render


În loc să fie reconstruit complet:
 nodurile Fiber sunt reutilizate;
 proprietățile sunt actualizate;
 React poate întrerupe și relua lucrul când este necesar.

Ce stochează un Fiber?
 State
 Props
 Hooks
 Side Effects
 Task-uri în așteptare

Structura Fiber
App Fiber

├── Header Fiber

├── Sidebar Fiber

└── Content Fiber

├── Card Fiber

└── Button Fiber

Beneficii pentru Fiber:


 UI mai fluid;
 animații mai bune;
 aplicații mai responsive.

Diffing Algorithm

Diffing este algoritmul care compară două arbori Virtual DOM și găsește
modificările minime necesare.

Regula 1: Tipuri diferite


<div> -> <section>
Șterge vechiul nod + Creează unul nou

Regula 2: Același tip


<button class="red"> -> <button class="blue">
Păstrează nodul + Actualizează atributul

Regula 3: Compararea copiilor


Vechi
A
B
C

Nou
A
B
D

Păstrează A
Păstrează B
Înlocuiește C cu D
Fluxul complet React in spatele scenei
State Update


Render Triggered


Nou Virtual DOM


Fiber Reconciler


Diffing Algorithm


Lista modificărilor


Commit Phase


Actualizare Real DOM


Browser Paint


UI actualizat

Commit Phase

După ce în Render Phase React a calculat toate modificările necesare, începe


Commit Phase.
În această etapă, React aplică efectiv modificările asupra DOM-ului real:
 adaugă elemente noi (insertions);
 actualizează elemente existente (updates);
 șterge elemente care nu mai sunt necesare (deletions).

Rolul React și al Renderer-ului


Un aspect important este că React nu modifică direct DOM-ul.
React se ocupă doar de:
 componente;
 state;
 props;
 Virtual DOM;
 reconciliere.
Pentru manipularea efectivă a platformei folosește un Renderer:
 pentru web → React DOM
 pentru mobile → React Native

Diagrama
React



Virtual DOM


Renderer

┌───────┴────────┐
│ │
▼ ▼
React DOM React Native
│ │
▼ ▼
Browser DOM Native UI

După reconciliere, React are o listă exactă de modificări.

Exemplu:

Actualizări necesare:
✓ Modifică textul
✓ Adaugă un buton
✓ Șterge un element

Aceste modificări sunt aplicate în DOM-ul real.

Diagrama

Virtual DOM Diff




Lista modificărilor


Commit Phase


Actualizare DOM

Commit este sincron


Spre deosebire de Render Phase (care poate fi întreruptă sau programată),
Commit Phase este:

 sincronă;
 executată dintr-o singură operație;
 nu poate fi întreruptă.

Motivul:
React trebuie să garanteze că utilizatorul nu vede o interfață incomplet
actualizată.

Diagrama
Render Phase
──────────────
Poate fi:
✓ întreruptă
✓ reluată
✓ prioritizată

Commit Phase
──────────────
✓ executare completă
✗ nu poate fi întreruptă
✗ nu poate fi împărțită

Actualizare Fiber Tree

În timpul renderizării există două arbori Fiber:


Current Fiber Tree


Work In Progress Tree

Pe parcursul Render Phase, React construiește și actualizează arborele Work In


Progress.

După Commit:

Înainte:

Current Tree


UI curentă

După Commit:

Work In Progress Tree




devine


Current Tree
Astfel, noua stare a aplicației devine baza pentru următorul ciclu de renderizare.

Browser Paint

După ce DOM-ul a fost actualizat, browserul preia controlul.


Acesta:
1. recalculează layout-ul;
2. aplică stilurile CSS;
3. execută procesul de paint;
4. afișează noua interfață pe ecran.

Diagramă

Commit Phase


DOM actualizat


Browser Layout


Browser Paint


Utilizatorul vede schimbarea

XXV. Class-based Components


În React, class-based components sunt o modalitate mai veche de a defini
componente folosind clase JavaScript. În aplicațiile React moderne, ele au fost în
mare parte înlocuite de function components și Hooks, dar încă apar în proiecte
mai vechi și este util să le înțelegi.
Ce este un class component?
Este o clasă care extinde [Link] și implementează metoda render().

import React from "react";


class Welcome extends [Link] {
render() {
return <h1>Salut, {[Link]}!</h1>;
}
}
export default Welcome;

Utilizare: <Welcome name="Andrei" />


Rezultat: <h1>Salut, Andrei!</h1>
- Metoda render() spune React-ului ce UI trebuie afișat pe ecran;
- render() doar returneaza codul jsx;
- se executa la apelarea componentei si dupa fiecare actualizare de state si
props

Props într-un class component


Props sunt accesate prin [Link].
class UserCard extends [Link] {
render() {
return (
<div>
<h2>{[Link]}</h2>
<p>{[Link]} ani</p>
</div>
);
}
}
 super() apelează constructorul clasei părinte.
 În JavaScript, dacă o clasă extinde o altă clasă și definește propriul
 constructor, trebuie să apelezi super() înainte de a folosi this.
 Dacă nu transmiți props către super, în versiunile mai vechi de React puteai
avea probleme la accesarea lui [Link] în constructor
State într-un class component
Un avantaj important al class components era gestionarea stării interne prin
state.

class Counter extends [Link] {


constructor(props) {
super(props);

[Link] = {
count: 0
};
}

render() {
return (
<div>
<p>Count: {[Link]}</p>
</div>
);
}
}

Modificarea state-ului
[Link] = 1; // Greșit

[Link]({
count: 1
});

Lifecycle Methods
Un motiv major pentru existența class components era accesul la ciclul de viață al
componentei.

1. componentDidMount() – se executa prin randare


componentDidMount() {
[Link]("Componenta a fost montată");
}
2. componentDidUpdate() – se executa dupa actualizarea componentei
componentDidUpdate(prevProps, prevState) {
if ([Link] !== [Link]) {
[Link]("Count s-a modificat");
}
}

3. componentWillUnmount() – se executa inainte ca componenta sa fie


eliminata
componentWillUnmount() {
[Link]("Curățare resurse");
}

Problema cu this
În class components trebuie să fii atent la contextul lui this.

Class component:
class Counter extends [Link] {
state = { count: 0 };
render() {
return (
<button
onClick={() =>
[Link]({
count: [Link] + 1
})
}
>
{[Link]}
</button>
);
}
}

Function component:
import { useState } from "react";
function Counter() {
const [count, setCount] = useState(0);

return (
<button onClick={() => setCount(count + 1)}>
{count}
</button>
);
}

XXVI. Combound Components


Definitie! În React, Compound Components este un pattern prin care mai multe
componente lucrează împreună pentru a forma un singur element complex,
împărțind starea și comportamentul între ele, fără ca utilizatorul componentei să
fie nevoit să treacă o mulțime de props.

Exemplu:
<select>
<option>România</option>
<option>Franța</option>
</select>

<select> și <option> sunt componente separate, dar funcționează împreună


ca o singură unitate.

Problema pe care o rezolvă

Să presupunem că vrei să construiești un component de tip Accordion.

Varianta clasică cu multe props:


<Accordion
items={[
{ title: "FAQ 1", content: "Răspuns 1" },
{ title: "FAQ 2", content: "Răspuns 2" }
]}
defaultOpen={0}
allowMultiple={false}
/>

Cu Compound Components:
<Accordion>
<[Link]>
<[Link]>FAQ 1</[Link]>
<[Link]>
Răspuns 1
</[Link]>
</[Link]>

<[Link]>
<[Link]>FAQ 2</[Link]>
<[Link]>
Răspuns 2
</[Link]>
</[Link]>
</Accordion>

Cum funcționează
De obicei se bazează pe:

1. Un component părinte
2. Componente copil
3. React Context pentru partajarea stării
Avantaje:
 API intuitiv
 Flexibilitate
 Evită "prop drilling"

Dezavantaje:
 Necesită Context
 Mai greu de urmărit pentru începători
 Dependință de structură

Exemplu complet: Tabs


import { createContext, useContext, useState } from "react";

const TabsContext = createContext();

function Tabs({ children }) {


const [activeTab, setActiveTab] = useState("1");

return (
<[Link]
value={{
activeTab,
setActiveTab,
}}
>
{children}
</[Link]>
);
}
function TabList({ children }) {
return <div>{children}</div>;
}

function Tab({ id, children }) {


const { activeTab, setActiveTab } =
useContext(TabsContext);

return (
<button
onClick={() => setActiveTab(id)}
style={{
fontWeight:
activeTab === id ? "bold" : "normal",
}}
>
{children}
</button>
);
}

function TabPanel({ id, children }) {


const { activeTab } =
useContext(TabsContext);

if (activeTab !== id) return null;

return <div>{children}</div>;
}

<Tabs>
<[Link]>
<[Link] id="1">Profil</[Link]>
<[Link] id="2">Setări</[Link]>
</[Link]>
<[Link] id="1">
Conținut profil
</[Link]>
<[Link] id="2">
Conținut setări
</[Link]>
</Tabs>

Render Props

Definitie! Render Props este un pattern React prin care o componentă nu decide
singură ce să afișeze, ci oferă date sau comportament unei funcții primite prin
props, iar acea funcție returnează JSX-ul care trebuie randat.
În loc să primești doar date prin props, primești acces direct la starea internă a
componentei.
Ideea de bază
O componentă expune ceva:
<DataProvider
render={(data) => (
<div>{[Link]}</div>
)}
/>

sau varianta mai populara:


<DataProvider>
{(data) => (
<div>{[Link]}</div>
)}
</DataProvider>

Funcția respectivă este numită render prop deoarece controlează ce se randează.

Exemplu
Să construim o componentă care urmărește poziția mouse-ului.
Fara Render Props:
function MouseTracker() {
const [position, setPosition] = useState({
x: 0,
y: 0,
});

useEffect(() => {
const handleMove = (e) => {
setPosition({
x: [Link],
y: [Link],
});
};

[Link]("mousemove", handleMove);
return () =>
[Link](
"mousemove",
handleMove
);
}, []);

return (
<div>
X: {position.x}
Y: {position.y}
</div>
);
}

Componenta decide singură cum afișează datele.

Cu Render Props:
function MouseTracker({ render }) {
const [position, setPosition] = useState({
x: 0,
y: 0,
});
useEffect(() => {
const handleMove = (e) => {
setPosition({
x: [Link],
y: [Link],
});
};

[Link]("mousemove", handleMove);

return () =>
[Link](
"mousemove",
handleMove
);
}, []);

return render(position);
}

<MouseTracker
render={({ x, y }) => (
<h1>
Mouse: {x}, {y}
</h1>
)}
/>
Poți schimba complet UI-ul fără să modifici componenta:
<MouseTracker
render={({ x, y }) => (
<div
style={{
position: "absolute",
left: x,
top: y,
}}
>
</div>
)}
/>
Varianta cu children
function MouseTracker({ children }) {
const [position, setPosition] = useState({
x: 0,
y: 0,
});

return children(position);
}
<MouseTracker>
{({ x, y }) => (
<p>
{x}, {y}
</p>
)}
</MouseTracker>

Un exemplu cu fetch data

function DataFetcher({
url,
children,
}) {
const [data, setData] = useState(null);
const [loading, setLoading] =
useState(true);
useEffect(() => {
fetch(url).then((res) => [Link]()).then((result) => {
setData(result);
setLoading(false);
});
}, [url]);
return children({
data,
loading,
});
}
<DataFetcher url="/api/users">
{({ data, loading }) => {
if (loading) {
return <p>Loading...</p>;
}
return (
<ul>
{[Link](user => (
<li key={[Link]}>
{[Link]}
</li>
))}
</ul>
);
}}
</DataFetcher>

Problema pe care o rezolva

Înainte de Hooks, reutilizarea logicii era dificilă. Existau două soluții principale:
 Higher-Order Components (HOC)
o withMouseTracker(MyComponent)
 Render Props
<MouseTracker>
{(mouse) => (
<MyComponent mouse={mouse} />
)}
</MouseTracker>

Debouncing

Definitie! Debouncing este o tehnică prin care amâni executarea unei funcții
până când utilizatorul încetează să mai declanșeze evenimentul pentru o anumită
perioadă de timp.

Este foarte utilă pentru evenimente care se produc foarte des:


 tastare într-un input
 redimensionarea ferestrei (resize)
 scroll
 mișcarea mouse-ului

Problema
Să presupunem că ai un câmp de căutare:
<input
onChange={(e) => {
search([Link]);
}}
/>
Daca utilizatorul scrie react, se vor face 5 apeluri: r, re, rea, reac, react.

Soluția: Debouncing

Cu un debounce de 500 ms se face un singur apel.


tasta r
tasta e
tasta a
tasta c
tasta t
(500ms fără tastare) => se execută search("react")

Cum funcționează
Ideea este:

1. utilizatorul declanșează un eveniment


2. pornești un timer
3. dacă apare un nou eveniment înainte de expirarea timerului:
o anulezi timerul vechi
o pornești unul nou
4. funcția rulează doar când timerul expiră

Implementare JavaScript simplă


function debounce(fn, delay) {
let timeoutId;

return (...args) => {


clearTimeout(timeoutId);

timeoutId = setTimeout(() => {


fn(...args);
}, delay);
};
}
const debouncedSearch = debounce(
(query) => {
[Link](query);
},
500
);
debouncedSearch("r");
debouncedSearch("re");
debouncedSearch("rea");
debouncedSearch("react");

După 500 ms se afișează doar: react

Exemplu in React
import { useState } from "react";

function Search() {
const [query, setQuery] = useState("");

return (
<input
value={query}
onChange={(e) =>
setQuery([Link])
}
/>
);
}

Varianta cu useEffect:
const [query, setQuery] = useState("");
const [debouncedQuery, setDebouncedQuery] =
useState("");

useEffect(() => {
const timer = setTimeout(() => {
setDebouncedQuery(query);
}, 500);
return () => {
clearTimeout(timer);
};
}, [query]);
Aici:
 fiecare modificare a lui query creează un timer nou
 cleanup-ul anulează timerul precedent
 doar ultima valoare supraviețuiește

useEffect(() => {
if (!debouncedQuery) return;

fetchUsers(debouncedQuery);
}, [debouncedQuery]);

Custom Hooks: useDebouncing


import {
useState,
useEffect,
} from "react";

function useDebounce(value, delay) {


const [debouncedValue,
setDebouncedValue] =
useState(value);

useEffect(() => {
const timer = setTimeout(() => {
setDebouncedValue(value);
}, delay);

return () => {
clearTimeout(timer);
};
}, [value, delay]);

return debouncedValue;
}

const debouncedQuery = useDebounce(query, 500);

XXVII. Testing
Definitie! Testing în React înseamnă verificarea faptului că aplicația ta
funcționează corect, atât la nivel de componente individuale, cât și la nivelul
întregii aplicații. În practică, testing-ul în React se împarte în mai multe categorii
și folosește diferite unelte.
1. De ce facem testing?
Scopurile principale sunt:
 Detectarea bug-urilor înainte de producție.
 Prevenirea regresiilor (ceva care funcționa înainte și se strică după o
modificare).
 Documentarea comportamentului aplicației.
 Creșterea încrederii când refactorizezi codul.

2. Tipuri de teste in React


1. Unit Tests – testeaza cea mai mica unitate de cod (ex: o functie, un hook, o
componenta simpla, etc…)
2. Component Tests – verifica daca o componenta se comporta corect
3. Integration Tests – testeaza colaborarea dintre mai multe componente
4. End-to-End (E2E) – simuleaza comportamentul unui utilizator real
- user intra pe site
- face login;
- adauga produs in cos;
- finalizeaza comanda;

3. Ecosistemul modern de testing in React


Cel mai folosit este Jest – framework de testare responsabil cu:
 rularea testelor
 assertions
 mocks
 coverage
4. Structura unui test in React

Componenta:
function Counter() {
const [count, setCount] = useState(0);
return (
<>
<p>{count}</p>
<button
onClick={() =>
setCount(count + 1)
}
>
Increment
</button>
</>
);
}

Test:
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";

test("increments count", async () => {


render(<Counter />);
const button = [Link]("button");
await [Link](button);
expect(
[Link]("1")
).toBeInTheDocument();
});
 test() -> defineste un caz de test, in cazul de fata verifica daca apasarea
butonului incrementeaza contorul
 async () => {} -> face functia asincrona si este necesar pentru ca folosim
userEvent;
 render() -> vine din React Testing Library si monteaza componenta intr-un
DOM virtual;
 screen -> reprezinta DOM-ul rezultat dupa render;
 getByRole() -> cauta un element dupa rolul sau semantic (ex: button, p, h1,
etc…); daca nu gaseste un element cu rolul specificat va returna eroare,
“Unable to find role="button”;
 userEvent -> este o bibliotecă care simulează utilizatorul real pentru că
reproduce mai fidel comportamentul browserului;
 click() -> simulează apăsarea butonului;
 await -> așteaptă până când click-ul și actualizările React sunt terminate;
 expected() -> este funcția principală de verificare;
 [Link]() -> caută un element care conține textul specificat;
 toBeInTheDocument() -> verifică dacă elementul există în DOM.

4. Testing pentru Hooks

Hook:
function useCounter() {
const [count, setCount] = useState(0);

return {
count,
increment: () =>
setCount(c => c + 1),
};
}

Test:
import { renderHook, act } from "@testing-library/react";
test("increments hook value", () => {
const { result } =
renderHook(() => useCounter());
act(() => {
[Link]();
});

expect(
[Link]
).toBe(1);
});

5. Mocking
Definitie! Mocking -> inlocuirea unor dependente reale cu versiuni controlate;

- Mock API Calls


o cod
fetch("/users");
o testare
[Link] = [Link](() =>
[Link]({
json: () =>
[Link]([
{ id: 1, name: "John" },
]),
})
);
- Mock Functions
o cod
const onClick = [Link]();
o testare
expect(onClick).toHaveBeenCalled();
- Mock Modules
o [Link]("./api"); / [Link]("./api");

6. Testing API Request


function Users() {
const [users, setUsers] = useState([]);

useEffect(() => {
fetch("/users")
.then(res => [Link]())
.then(setUsers);
}, []);
return (
<>
{[Link](user => (
<p key={[Link]}>
{[Link]}
</p>
))}
</>
);
}
render(<Users />);

expect(
await [Link]("John")
).toBeInTheDocument();

7. Testing pentru React Router


<Route path="/profile" element={<Profile />} />

import { MemoryRouter } from "react-router-dom";


render( <MemoryRouter> <Profile /> </MemoryRouter> );

React Testing Library

1. Testeaza comportamentul, nu implementarea


Principiul central al React Testing Library: Cu cât testele seamănă mai mult cu
modul în care utilizatorul folosește aplicația, cu atât sunt mai valoroase.

- evita: expect([Link]).toBe(1);
o Utilizatorul nu vede state-ul.
- prefera: expect([Link]("1")).toBeInTheDocument();
o Utilizatorul vede textul afișat.

2. Gandeste ca un utilizator
Întreabă-te:
 Ce vede utilizatorul?
 Ce poate apăsa?
 Ce poate introduce?
 Ce mesaj apare?

3. Ordinea unui test: Arrange -> Act -> Assert


- Arrange -> pregateste componenta (ex: render(<LoginForm />));
- Act -> simuleaza o actiune (ex: await [Link](button));
- Assert -> verifica rezultatul (ex: expect(message).toBeInTheDocument());

4. userEvent vs fireEvent
- userEvent -> imita comportamentul browser-ului mult mai realist decat
alte metode;
- fireEvent -> trimite evenimentul instantaneu catre element;

5. getBy vs queryBy vs findBy


- getBy -> elementul trebui sa exista acum, altfel fail;
- queryBy -> elementul poate sa nu existe, altfel null;
- findBy -> pentru datele incarcate asincron;

6. Fiecare test trebuie sa fie indepent si sa aiba propriul render(…)

XXIII. Redux
Definitie! Redux este o librărie de gestionare a stării (state management)
pentru aplicații JavaScript, utilizată cel mai des împreună cu React pentru a
stoca și manageria datele globale ale unei aplicații într-un mod predictibil.
În aplicațiile mari de React, transmiterea datelor de la un component părinte la
un component copil situat la câteva niveluri distanță devine dificilă (fenomen
numit prop drilling). Redux rezolvă această problemă prin mutarea întregii stări
globale într-un singur loc centralizat numit Store, de unde orice component din
aplicație poate citi sau actualiza datele direct.

Cei 3 piloni principali


Fluxul de date în Redux este strict unidirecțional și se bazează pe trei elemente
interconectate:
 Store: Containerul global și unic în care se află starea întregii aplicații.
Starea este imutabilă (nu poate fi modificată direct).
 Actions (Acțiuni): Obiecte JavaScript simple care descriu ce s-a întâmplat
în aplicație (de exemplu: adăugarea unui produs în coș). Fiecare acțiune are
obligatoriu o proprietate type.
 Reducers (Reducători): Funcții pure care primesc starea curentă și o
acțiune, iar apoi returnează o nouă stare modificată pe baza acelei acțiuni
Exemplu: Să presupunem că construim o aplicație de tip Coș de cumpărături.

1. Action

Este un obiect JavaScript simplu care descrie ce vrei să faci. Are obligatoriu un
câmp type (o etichetă text) și, opțional, un payload (datele trimise).
// O acțiune pentru adăugarea unui produs în coș
const addToCartAction = {
type: 'cart/addItem',
payload: { id: 101, name: 'Căști Wireless', price: 299 }
};

2. Reducer-ul (Procesatorul)

Este o funcție care primește starea curentă și acțiunea trimisă, apoi calculează
noua stare.
Regulă de aur: Reducer-ul nu modifică (mutate) starea veche, ci creează o copie
complet nouă a acesteia.

const initialState = { items: [], total: 0 };

function cartReducer(state = initialState, action) {


switch ([Link]) {
case 'cart/addItem':
// Returnăm un obiect NOU (folosind operatorul spread '...')
return {
...state,
items: [...[Link], [Link]],
total: [Link] + [Link]
};
default:
return state; // Dacă nu recunoaște acțiunea, returnează starea neschimbată
}
}

3. Store-ul (Depozitul)

Este obiectul central care leagă acțiunile și reducerii. Acesta deține starea
aplicației și oferă câteva metode cheie:
 dispatch(action) -> Trimite o acțiune către reducer (echivalentul trimiterii
formularului la casier).
 getState() -> Returnează starea curentă.
 subscribe(listener) -> Permite aplicației să asculte când se modifică starea
pentru a reîncărca interfața grafică.

Cum se conectează Redux cu React?


În mod nativ, Redux nu știe ce este React. Pentru a le face să colaboreze, folosim
librăria oficială React-Redux, care ne pune la dispoziție două unelte principale
(Hook-uri):
1. useSelector: Permite unei componente React să extragă exact datele de care are
nevoie din Store-ul global.
2. useDispatch: Oferă componentei acces la funcția dispatch pentru a trimite
acțiuni

import React from 'react';


import { useSelector, useDispatch } from 'react-redux';
export function CartSummary() {
// 1. Citim datele din Store
const totalItems = useSelector((state) => [Link]);
const totalPrice = useSelector((state) => [Link]);
const dispatch = useDispatch();
const handleClearCart = () => {
// 2. Trimitem o acțiune pentru a goli coșul
dispatch({ type: 'cart/clear' });
};
return (
<div>
<p>Ai {totalItems} produse în coș.</p>
<p>Total: {totalPrice} RON</p>
<button onClick={handleClearCart}>Golește Coșul</button>
</div>
);
}
Ce este Redux Toolkit (RTK) și de ce contează?

În trecut, Redux era criticat pentru că rula foarte mult cod repetitiv (boilerplate).
Trebuia să configurezi manual Store-ul, să scrii fișiere separate pentru acțiuni,
constante și reduceri.

Astăzi, echipa Redux a creat Redux Toolkit (RTK). Acesta simplifică totul prin
conceptul de Slice (o felie din starea aplicației). Un Slice generează automat
acțiunile și reducerii în același loc și folosește sub capotă o librărie numită Immer,
care îți permite să scrii cod care pare că modifică direct starea, dar o face în
siguranță.
import { createSlice } from '@reduxjs/toolkit';
const cartSlice = createSlice({
name: 'cart',
initialState: { items: [], total: 0 },
reducers: {
addItem: (state, action) => {
// Cu RTK poți folosi direct .push(), se ocupă Immer de imutabilitate!
[Link]([Link]);
[Link] += [Link];
},
clear: (state) => {
[Link] = [];
[Link] = 0;
}
}
});
export const { addItem, clear } = [Link];
export default [Link];
Exemplu complet: Cos de cumparaturi

Pasul 1: Instalarea librăriilor


În terminalul proiectului tău React, rulează următoarea comandă pentru a instala
Redux Toolkit și legătura sa cu React:
npm install @reduxjs/toolkit react-redux
Pasul 2: Crearea "Feliei" de Stare (The Slice)
Creăm un fișier numit [Link]. Acesta va conține starea inițială, acțiunile și
reducerii pentru coșul nostru.
// [Link]
import { createSlice } from '@reduxjs/toolkit';

const initialState = {
items: [], // Aici vom salva produsele adăugate
};

const cartSlice = createSlice({


name: 'cart',
initialState,
reducers: {
// Acțiunea de adăugare produs
addItem: (state, action) => {
[Link]([Link]); // RTK ne permite să folosim .push direct
},
// Acțiunea de ștergere produs după ID
removeItem: (state, action) => {
[Link] = [Link](item => [Link] !== [Link]);
}
}
});
// Exportăm acțiunile generate automat pentru a le folosi în componente
export const { addItem, removeItem } = [Link];

// Exportăm reducer-ul pentru a-l adăuga în Store


export default [Link];
Pasul 3: Configurarea Depozitului Central (The Store)
Creăm un fișier numit [Link] unde colectăm toți reducerii din aplicație.
// [Link]
import { configureStore } from '@reduxjs/toolkit';
import cartReducer from './cartSlice';

export const store = configureStore({


reducer: {
cart: cartReducer, // Adăugăm felia noastră de coș în store-ul global
},
});

Pasul 4: Conectarea Store-ului la Aplicația React


Mergem în fișierul principal al aplicației (de regulă [Link] sau [Link]) și
împachetăm aplicația într-un <Provider>, oferindu-i acces la store.
// [Link] sau [Link]
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import { store } from './store';
import { Provider } from 'react-redux'; // Importăm Provider-ul

[Link]([Link]('root')).render(
<[Link]>
{/* Oferim acces întregii aplicații la Store-ul Redux */}
<Provider store={store}>
<App />
</Provider>
</[Link]>
);

Pasul 5: Utilizarea în Componente (Citire și Trimitere Date)


Acum putem crea componentele noastre React care vor interacționa cu Redux
prin hook-urile useSelector și useDispatch.
Componenta A: Lista de Produse (Trimite date către Redux)
// [Link]
import React from 'react';
import { useDispatch } from 'react-redux';
import { addItem } from './cartSlice';
const PRODUCTS = [
{ id: 1, name: 'Căști Bluetooth', price: 150 },
{ id: 2, name: 'Tastatură Mecanică', price: 350 },
];
export function ProductList() {
const dispatch = useDispatch(); // Unealtă pentru a trimite acțiuni

return (
<div style={{ border: '1px solid gray', padding: '15px', margin: '10px' }}>
<h2>Produse Disponibile</h2>
{[Link](product => (
<div key={[Link]} style={{ marginBottom: '10px' }}>
<span>{[Link]} - {[Link]} RON </span>
{/* Tritem produsul ca payload în acțiunea addItem */}
<button onClick={() => dispatch(addItem(product))}>
Adaugă în coș
</button>
</div>
))}
</div>
);
}

Componenta B: Coșul de Cumpărături (Citește și Modifică datele din Redux)


// [Link]
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { removeItem } from './cartSlice';
export function Cart() {
// Extragem lista de produse din store-ul global
const cartItems = useSelector((state) => [Link]);
const dispatch = useDispatch();

// Calculăm prețul total direct din starea extrasă


const totalPrice = [Link]((sum, item) => sum + [Link], 0);

return (
<div style={{ border: '1px solid green', padding: '15px', margin: '10px' }}>
<h2>Coșul Tău ({[Link]} produse)</h2>
{[Link] === 0 ? (
<p>Coșul este gol.</p>
):(
<ul>
{[Link]((item, index) => (
<li key={index} style={{ marginBottom: '5px' }}>
{[Link]} - {[Link]} RON{' '}
{/* Trimitem doar ID-ul produsului pentru ștergere */}
<button onClick={() => dispatch(removeItem([Link]))}>
Elimină
</button>
</li>
))}
</ul>
)}
<h3>Total: {totalPrice} RON</h3>
</div>
);
}

Pasul 6: Afișarea componentelor în [Link]


În final, punem ambele componente în [Link] pentru a le vedea cum comunică
instant între ele prin intermediul Redux, fără să transmitem prop-uri de la una la
alta.
// [Link]
import React from 'react';
import { ProductList } from './ProductList';
import { Cart } from './Cart';
function App() {
return (
<div style={{ fontFamily: 'sans-serif', maxWidth: '600px', margin: '0 auto' }}>
<h1>Magazinul Meu Virtual</h1>
<ProductList />
<Cart />
</div>
);
}

export default App;

Cum funcționează acest flux în realitate?


1. Dai click pe „Adaugă în coș” în ProductList.
2. useDispatch trimite obiectul { type: 'cart/addItem', payload: product }.
3. cartSlice prinde acțiunea și adaugă produsul în lista [Link].
4. useSelector din componenta Cart observă că starea s-a schimbat, extrage noile
date și forțează componenta să se redeseneze pe ecran instant.

XXVIII. useDispatch

1. Ce este mai exact useDispatch()?


În arhitectura Redux, datele tale nu pot fi modificate direct. Pentru a schimba
starea, o componentă trebuie să „trimită” (dispatch) un mesaj.

Acest mesaj se numește Acțiune (Action).


 useDispatch este o funcție care, atunci când este apelată într-o componentă,
îți returnează o referință către însăși funcția dispatch din store-ul Redux.
 Tu folosești această referință pentru a pasa acțiunile tale către reduceri
(reducers).

2. Cum se folosește? (Exemplu practic)


Pentru a utiliza useDispatch, îl imporți din react-redux, îl apelezi pentru a obține
funcția, iar apoi o apelezi pe aceasta din urmă pasându-i acțiunea ca argument.

import React from 'react';


import { useDispatch } from 'react-redux';
// Importăm acțiunea pe care dorim să o declanșăm
import { increment } from './counterSlice';

export function Counter() {


// 1. Obținem referința către dispatch
const dispatch = useDispatch();

return (
<div>
{/* 2. Trimitem acțiunea când utilizatorul dă click */}
<button onClick={() => dispatch(increment())}>
Mărește valoarea
</button>
</div>
);
}

3. Conceptele Fundamentale
Pentru a înțelege și stăpâni useDispatch, trebuie să fii familiarizat cu câteva
concepte de bază din ecosistemul Redux:
A. Acțiunile (Actions)
O acțiune este un simplu obiect JavaScript care are o proprietate type
(obligatorie) și, opțional, date suplimentare (de obicei numite payload).
// O acțiune simplă
{
type: 'counter/increment'
}

// O acțiune cu payload
{
type: 'counter/incrementByAmount',
payload: 5
}

Notă: Când folosești Redux Toolkit, nu trebuie să creezi aceste obiecte manual; ele
sunt generate automat de către "slice-uri".
B. Reducerii (Reducers)
Reducerii sunt funcții pure care preiau starea curentă și o acțiune, iar pe baza
tipului de acțiune (type), calculează și returnează noua stare.
dispatch trimite acțiunea direct către reduceri.
C. Store-ul (The Store)
Este depozitul central care ține toată starea aplicației. Când faci
dispatch(acțiune), store-ul primește acțiunea, rulează reducerul corespunzător și
actualizează starea.
D. Funcții asincrone (Thunks)
Dacă ai nevoie să faci o operațiune asincronă (cum ar fi un apel la un API/server)
înainte de a face dispatch (de exemplu, afișezi un status de "loading", aștepți
datele, apoi faci dispatch cu succes), folosești Thunks. În Redux Toolkit, acestea
sunt create cu createAsyncThunk și trimise exact la fel:
dispatch(fetchUserData()).
4. Bune practici (Best Practices)
 Folosește acțiuni pregătite (Action Creators): În loc să scrii manual dispatch({
type: 'increment' }), folosește funcțiile exportate de slice-urile tale:
dispatch(increment()). Acest lucru previne greșelile de scriere și centralizează
logica.
 Tipizarea în TypeScript (dacă folosești TS): În aplicațiile mari, este o practică
standard să creezi un hook personalizat de dispatch pentru a asigura tipizarea
corectă (ex: incluzând tipurile pentru Thunk-uri)

// Într-un fișier [Link] (pentru TypeScript)


import { useDispatch } from 'react-redux';
import type { AppDispatch } from './store';

export const useAppDispatch: () => AppDispatch = useDispatch;

// În componentă:
const dispatch = useAppDispatch();

XXIX. „Thunk”
1. Ce înseamnă, de fapt, un „Thunk”?
În programare, termenul general de „thunk” reprezintă o funcție care amână o
evaluare sau o execuție până mai târziu.
În loc să execute o bucată de cod imediat, o împachetezi într-o funcție pe care o
poți apela exact atunci când ai nevoie.

2. Cum funcționează un Thunk în Redux?


Când folosești middleware-ul Thunk, mecanismul de dispatch își schimbă
comportamentul:
1. Dacă trimiți (dispatch) un obiect simplu, Redux îl trimite direct la reduceri
(comportamentul standard).
2. Dacă trimiți (dispatch) o funcție (un Thunk), middleware-ul interceptează acea
funcție, o execută și îi oferă două argumente extrem de importante: dispatch și
getState.
Datorită acestor argumente, funcția ta asincronă poate trimite alte acțiuni
sincrone pe parcurs (de exemplu, o acțiune pentru „în faza de încărcare” și o alta
pentru „succes”).

3. Anatomia unui Thunk (Cum se scrie)


În prezent, există două moduri de a scrie un Thunk: modul modern (folosind
Redux Toolkit) și modul clasic (Redux simplu).
Varianta Modernă: createAsyncThunk (Redux Toolkit)
Aceasta este metoda standard utilizată astăzi în industrie deoarece generează
automat acțiunile pentru stările de pending, fulfilled și rejected.
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
// 1. Definim Thunk-ul asincron
export const fetchUserById = createAsyncThunk(
'users/fetchById', // Numele acțiunii (prefixul)
async (userId, thunkAPI) => {
// Aici scriem codul asincron (apelul API)
const response = await fetch(`[Link]
const data = await [Link]();

// Ceea ce returnăm aici devine proprietatea `payload` în acțiunea finalizată


return data;
}
);
// 2. Gestionăm stările în Slice prin "extraReducers"
const usersSlice = createSlice({
name: 'users',
initialState: { data: null, loading: false, error: null },
reducers: {},
extraReducers: (builder) => {
builder
// Când apelul API pornește
.addCase([Link], (state) => {
[Link] = true;
})
// Când apelul API s-a terminat cu succes
.addCase([Link], (state, action) => {
[Link] = false;
[Link] = [Link]; // Datele de la server sunt aici
})
// Când apelul API a eșuat
.addCase([Link], (state, action) => {
[Link] = false;
[Link] = [Link];
});
},
});

export default [Link];

Varianta Clasică: Thunk scris manual (Fără Toolkit)


Pentru a înțelege exact ce se întâmplă sub capotă, iată cum arată un Thunk scris
manual ca o funcție care returnează o altă funcție:
// O funcție exterioară care primește parametrii tăi (ex: userId)
export function fetchUserClassic(userId) {
// Returnează funcția Thunk interoptată de middleware
return async function(dispatch, getState) {
// Putem trimite o acțiune sincronă pentru a arăta un loader pe ecran
dispatch({ type: 'user/fetchStarted' });

try {
const response = await fetch(`[Link]
const data = await [Link]();
// Dacă totul e ok, trimitem datele către reducer
dispatch({ type: 'user/fetchSuccess', payload: data });
} catch (error) {
// Dacă apare o eroare, trimitem eroarea
dispatch({ type: 'user/fetchFailed', payload: [Link] });
}
};
}

4. Cum se utilizează Thunk-ul în Componentă?


În interiorul componentei tale de React, utilizarea unui Thunk este complet
identică cu trimiterea unei acțiuni obișnuite. Folosești tot hook-ul useDispatch().
import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { fetchUserById } from './usersSlice';
export function UserProfile({ userId }) {
const dispatch = useDispatch();
// Extragem starea din store-ul global
const { data, loading, error } = useSelector((state) => [Link]);
useEffect(() => {
// Trimitem Thunk-ul la fel ca pe orice altă acțiune
dispatch(fetchUserById(userId));
}, [dispatch, userId]);
if (loading) return <p>Se încarcă datele utilizatorului...</p>;
if (error) return <p>Eroare: {error}</p>;
if (!data) return null;
return (
<div>
<h1>{[Link]}</h1>
<p>Email: {[Link]}</p>
</div>
);
}

5. Rezumatul fluxului de date cu Thunk


Fără Thunk, fluxul este strict sincron:
\(\text{Click\ în\ Componentă}\longrightarrow \text{Dispatch(Action\
Object)}\longrightarrow \text{Reducer}\longrightarrow \text{Update\ Store}\)
Cu Thunk, fluxul devine capabil de operațiuni asincrone:
1. Componenta apelează dispatch(fetchUserById(5)).
2. Thunk Middleware observă că ai trimis o funcție și o oprește din drumul spre
reduceri.
3. Thunk-ul rulează codul asincron (ex: cererea HTTP).
4. În timpul rulării, Thunk-ul face dispatch la acțiuni simple (obiecte) în momente
cheie:
o Trite o acțiune când începe (pending).
o Trimite o acțiune când primește răspunsul (fulfilled).
5. Reducerii primesc aceste obiecte simple, actualizează starea, iar Componenta se
randează din nou cu noile date.

XXX. Normalizarea datelor in aplicatiile React

Definiție
Normalizarea datelor reprezintă procesul prin care informațiile provenite din
surse externe (API, fișiere JSON, baze de date sau alte componente) sunt
transformate într-o structură internă unitară, predictibilă și compatibilă cu
cerințele aplicației.
Într-o aplicație React, normalizarea are rolul de a asigura faptul că toate
componentele primesc date într-un format consistent, eliminând necesitatea
verificărilor repetate pentru multiple structuri de date posibile.

De ce este necesară normalizarea?


Datele provenite din surse externe nu au întotdeauna aceeași structură.
Exemplu:
"email"
sau:
{
key: "email",
label: "Email"
}
sau:
{
value: "email",
text: "Email"
}
Toate aceste valori reprezintă aceeași informație, însă aplicația ar trebui să
gestioneze o singură structură internă.
Prin normalizare, toate variantele sunt transformate într-un format standard:
{
key: "email",
label: "Email"
}
Astfel, componentele React pot utiliza aceeași logică indiferent de sursa inițială a
datelor.

Normalizarea prin funcții dedicate


În implementările moderne, normalizarea este realizată prin funcții specializate
numite normalizatori.
Un normalizator primește o valoare într-un format necunoscut sau variabil și
returnează o valoare într-un format stabilit de aplicație.

Exemplu:
function normalizeOption(value) {
if (!value) return null;

if (typeof value === "string") {


return {
key: value,
label: value
};
}

return {
key: [Link],
label: [Link] ?? [Link]
};
}
Această funcție creează un contract intern de date:
{
key: string,
label: string
}
Toate componentele care folosesc aceste date pot presupune existența aceleiași
structuri.
Normalizarea în fluxul unei aplicații React
Într-o aplicație React, normalizarea este de obicei realizată la intrarea datelor în
aplicație:

Sursă externă
(JSON / API / Database)
|

Normalizare date
|

State React
|

Componente UI
|

Interacțiunea utilizatorului
|

Pregătire date pentru salvare
|

API / Database
Prin această abordare, componentele de interfață nu trebuie să cunoască
formatul inițial al datelor.

Exemplu practic
Un câmp de tip radio poate veni dintr-un fișier JSON astfel:
{
warranties: {
key: "radio-2",
value: "Garanție standard"
}
}
Componenta React poate avea nevoie însă de:
{
warranties: {
key: "radio-2",
label: "Garanție standard"
}
}
Funcția de normalizare realizează conversia:
normalizeOption(warranties)
Rezultatul este o structură compatibilă cu componenta:
<input
type="radio"
checked={[Link] === [Link]}
/>

Beneficiile normalizării
1. Consistență
Toate componentele lucrează cu același format de date.

2. Reducerea complexității
Logica de conversie este centralizată într-un singur loc, nu repetată în fiecare
componentă.

3. Mentenabilitate
Modificarea structurii datelor externe necesită schimbări doar în funcțiile de
normalizare.

4. Reducerea erorilor
Se evită situațiile în care o componentă așteaptă un obiect, dar primește un
string sau o altă structură.

5. Separarea responsabilităților
Componentele React se concentrează pe afișare și interacțiune, iar funcțiile de
normalizare se ocupă de transformarea datelor.

S-ar putea să vă placă și