React Hooks — ce sunt, hook-uri principale și reguli de utilizare

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

Hook-urile React — sunt funcții care permit utilizarea stării, efectelor, contextului și altor capabilități React în componente funcționale, fără a scrie clase. Introduse în React 16.8, hook-urile au schimbat radical abordarea de dezvoltare, înlocuind modele complexe precum HOC și render props. Potrivit React, 2024, există 15 hook-uri încorporate care acoperă toate scenariile de gestionare a componentelor.

Principalele

  • Hook-urile — funcții pentru conectarea capabilităților React la componente funcționale fără extends Component.
  • useState — hook de bază pentru gestionarea stării, returnează valoarea și setter-ul.
  • useEffect — pentru efecte secundare: cereri API, abonamente, temporizatoare, manipulări DOM.
  • useContext — pentru acces la contextul React fără împliări Consumer și static contextType.
  • Regulile hook-urilor — apelați doar la nivelul superior și doar în interiorul componentelor funcționale sau hook-urilor personalizate.

Ce sunt hook-urile React?

Hook-urile React — un set de funcții încorporate în React care permit „conectarea” la mecanismele interne ale framework-ului fără a crea clase. Înainte de React 16.8, componentele funcționale puteau doar să primească props și să returneze JSX — orice logică complexă necesita o componentă de clasă.

Problema componentelor de clasă — separarea logicii conexe. Codul referitor la o singură entitate (de exemplu, abonamentul WebSocket) era forțat să fie împărțit între componentDidMount, componentDidUpdate și componentWillUnmount. Hook-urile rezolvă aceasta: toată logica se grupează într-un singur useEffect.

A doua problemă — reutilizarea logicii. Pentru reutilizarea logicii de stare între componentele de clasă trebuiau aplicate HOC (componente de ordin superior) sau render props — ambele modele creau împliare suplimentare în arborele componentelor. Hook-urile personalizate rezolvă această problemă fără împliere.

useState — gestionarea stării

useState — cel mai de bază hook. Acceptă o valoare inițială și returnează un tuplu: valoarea curentă și o funcție pentru actualizarea ei. Spre deosebire de this.setState în clase, useState nu îmbină obiecte — noua stare înlocuiește complet precedenta pentru o anumită apelare a useState.

js
import React, { useState } from 'react';

function Form() {
  const [text, setText] = useState('');
  const [submitting, setSubmitting] = useState(false);

  async function handleSubmit() {
    setSubmitting(true);
    await fetch('/api/submit', {
      method: 'POST',
      body: JSON.stringify({ text })
    });
    setSubmitting(false);
  }

  return (
    <div>
      <input
        value={text}
        onChange={e => setText(e.target.value)}
      />
      <button onClick={handleSubmit}
        disabled={submitting}>
        {submitting ? 'Saving...' : 'Submit'}
      </button>
    </div>
  );
}

Forma funcțională a setState

Setter-ul useState poate accepta o funcție care primește valoarea anterioară și returnează una nouă. Forma funcțională este obligatorie când noua stare se calculează din cea anterioară și sunt posibile apelări în lot ale setState — fiecare apelare va primi valoarea anterioară actualizată.

useEffect — efecte secundare și ciclu de viață

useEffect — hook pentru executarea efectelor secundare în componente funcționale. Acceptă două argumente: o funcție-efect și un tablou de dependențe. React execută efectul după fiecare randare, dar la specificarea dependențelor — doar când oricare dintre ele s-a schimbat.

Un tablou gol de dependențe [] înseamnă că efectul se execută o singură dată după prima randare (analog cu componentDidMount). Returnarea unei funcții din efect este cleanup-ul, care se execută la demontare (analog cu componentWillUnmount) și înainte de re-executarea efectului.

js
import React, { useState, useEffect } from 'react';

function ChatRoom({ roomId }) {
  const [messages, setMessages] = useState([]);

  useEffect(() => {
    const socket = new WebSocket(`wss://chat/${roomId}`);

    socket.onmessage = (event) => {
      setMessages(prev => [...prev, JSON.parse(event.data)]);
    };

    // Cleanup - închide socket
    return () => socket.close();
  }, [roomId]);

  return <ul>
    {messages.map((msg, i) =>
      <li key={i}>{msg.text}</li>
    )}
  </ul>;
}

Dependențele useEffect — sunt singura legătură între efect și lumea exterioară. Dacă în efect este utilizată o variabilă din props sau stare, aceasta trebuie să fie în tabloul de dependențe. Ignorarea acestei reguli este sursa de bug-uri cu date învechite (stale closure).

useContext — acces la context

useContext — hook pentru citirea valorii contextului. Acceptă obiectul de context creat de React.createContext și returnează valoarea curentă. Dacă contextul se schimbă, componenta se re-renderează automat cu noua valoare.

Apariția useContext, accesul la context în componente funcționale necesita împlierea în Context.Consumer, ceea ce crea împliere. useContext elimină complet Consumer — este suficient să apelați hook-ul în orice loc al componentei funcționale.

js
import React, { createContext, useContext } from 'react';

const ThemeContext = createContext('luminos');

function ThemedButton() {
  const theme = useContext(ThemeContext);

  return <button
    className={`btn btn-${theme}`}
  >
    {theme === 'dark' ? 'moon' : '☀️'}
  </button>;
}

useContext nu înlocuiește Redux sau starea globală. React recomandă utilizarea contextului pentru valori rareori schimbate — temă, limbă, autorizare. Pentru date actualizate frecvent, fiecare re-render va recalcula toți consumatorii contextului, ceea ce poate reduce performanța.

useReducer — logică complexă a stării

useReducer — alternativă la useState pentru stări cu logică complexă de actualizare. Acceptă o funcție reducer și o stare inițială, returnează starea curentă și o funcție dispatch. Reducer primește starea curentă și acțiunea, returnează o stare nouă — o funcție pură fără efecte secundare.

useReducer este util în special când mai multe acțiuni (set, reset, increment, addItem) gestionează o singură stare. În loc de mai multe useState cu setteri diferiți — un singur reducer cu tipuri de acțiuni clare. Aceasta face logica previzibilă și ușor de testat.

js
import React, { useReducer } from 'react';

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { ...state, count: state.count + 1 };
    case 'setStep':
      return { ...state, step: action.step };
    case 'reset':
      return { count: 0, step: 1 };
    default:
      throw new Error('Unknown action: ' + action.type);
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, {
    count: 0,
    step: 1
  });

  return (
    <>
      <p>Count: {state.count}</p>
      <button
        onClick={() => dispatch({ type: 'increment' })}>+</button>
      <button
        onClick={() => dispatch({ type: 'reset' })}>Reset</button>
    </>
  );
}

useRef, useMemo, useCallback — optimizare

useRef creează un obiect mutabil care se menține între randări. Modificarea current nu provoacă re-rendare. Utilizarea principală — acces la elemente DOM și stocarea valorilor mutabile (temporizatoare, valori anterioare, instanțe de biblioteci).

useMemo memoizează rezultatul funcției. Recalculează valoarea doar când se schimbă una dintre dependențe. Folosiți pentru calcule costisitoare — filtrarea tablourilor mari, sortare, formatarea datelor. useCallback — un caz particular al useMemo pentru memoizarea funcțiilor.

js
import React, { useRef, useMemo, useCallback } from 'react';

function SearchPage({ items, query, onItemSelect }) {
  const inputRef = useRef(null);

  // Autofocus la montare
  useEffect(() => {
    inputRef.current?.focus();
  }, []);

  // Memoizează rezultatul filtrat
  const filtered = useMemo(() => {
    return items.filter(item =>
      item.name.toLowerCase()
        .includes(query.toLowerCase())
    );
  }, [items, query]);

  // Memoizează callback
  const handleSelect = useCallback((id) => {
    onItemSelect(id);
  }, [onItemSelect]);

  return (
    <div>
      <input ref={inputRef}
        value={query}
        onChange={e => setQuery(e.target.value)}
      />
      <ul>
        {filtered.map(item =>
          <li key={item.id}>
            <button
              onClick={() => handleSelect(item.id)}>
              {item.name}
            </button>
          </li>
        )}
      </ul>
    </div>
  );
}

Regulă importantă: useRef, useMemo și useCallback — instrumente de optimizare, nu construcții obligatorii. Nu împlecați totul în useMemo — măsurați mai întâi performanța. Optimizarea prematură complică codul. React randază componente rapid; useMemo este necesar doar când calculele sunt cu adevărat costisitoare (O(n²) și mai sus).

Regulile hook-urilor și erori frecvente

React a introdus două reguli stricte pentru hook-uri. Prima: apelați hook-urile doar la nivelul superior — nu în interiorul condițiilor, buclelor sau funcțiilor împliate. A doua: apelați hook-urile doar din componente funcționale React sau hook-uri personalizate. Plugin-ul ESLint eslint-plugin-react-hooks verifică automat ambele reguli.

Cea mai frecventă eroare — încălcarea ordinii de apelare a hook-urilor. React se bazează pe ordinea de apelare a hook-urilor între randări. Dacă un hook se află într-o condiție și nu a fost apelat într-una dintre randări, toate hook-urile ulterioare se deplasează, provocând bug-uri.

EroareCe se întâmplăSoluție
Hook în condițieOrdinea hook-urilor se stricăTransferați logica condiției în interiorul hook-ului
Stale closureEfectul folosește o valoare vecheAdăugați toate dependențele în tablou
Buclă infinităuseEffect fără dependențe modifică stareaSpecificați dependențele sau folosiți useReducer
Modificarea ref în randareEfect secundar în corpul funcțieiMutați mutațiile în useEffect

A doua eroare frecventă — stale closure în useEffect. Dacă în efect este folosită o variabilă nespecificată în dependențe, efectul „împreuna” valoarea ei la momentul creării închiderii. Soluția — includeți întotdeauna toate variabilele utilizate în tabloul de dependențe. Dacă sunt prea multe dependențe, împărți efectul în mai multe.

Întrebări frecvente

De ce hook-urile nu pot fi apelate în condiții?

React se bazează pe ordinea de apelare a hook-urilor între randări. Dacă un hook este apelat într-o condiție și nu a funcționat într-una dintre randări, toate hook-urile ulterioare „alunecă” — starea lor se amestecă. Regula ESLint hooks/exhaustive-deps avertizează despre aceasta.

Care este diferența între useEffect și useLayoutEffect?

useLayoutEffect este sincron și se execută înainte ca browserul să redeze modificările. useEffect este asincron — după redare. Folosiți useLayoutEffect doar când trebuie să măsurați sau să modificați DOM-ul înainte ca utilizatorul să vadă schimbările (poziția popover, restaurarea scroll-ului).

Cum să anulați o cerere fetch la demontare?

Folosiți AbortController în interiorul useEffect. Creați un controller, transmiteți signal în fetch și în funcția cleanup apelați controller.abort(). Pentru browsere vechi este suficient un flag cancelled: return () => { cancelled = true; } cu verificare înainte de setState.

Ce este un hook personalizat și când să îl creați?

Un hook personalizat — o funcție JavaScript cu prefixul use care utilizează hook-uri încorporate. Creați-l când aceeași logică cu hook-uri se repetă în două sau mai multe componente. Exemple: useDebounce, useLocalStorage, useMediaQuery. Hook-urile personalizate înlocuiesc HOC și render props.

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

În Strict Mode, React montează și demontează intenționat componenta de două ori în modul de dezvoltare pentru a găsi erori cu efecte necurățate. Dacă cleanup-ul este scris corect, apelarea dublă nu cauzează probleme. În modul de producție acest lucru nu se întâmplă.

Rezumat

  • Hook-urile React — funcții pentru utilizarea stării, efectelor și contextului în componente funcționale, care au înlocuit clasele.
  • useState — hook de bază pentru starea locală cu setter funcțional pentru actualizări sigure.
  • useEffect — hook universal pentru efecte secundare cu tablou de dependențe și funcție de cleanup.
  • useContext — acces direct la contextul React fără împliări Consumer și împliere suplimentară.
  • useReducer — pentru logică complexă a stării cu tipuri de acțiuni, alternativă la Redux într-o singură componentă.
  • useRef, useMemo, useCallback — instrumente de optimizare: referințe DOM, memoizarea valorilor și callback-urilor.
  • Regula nivelului superior — restricție strictă de apelare a hook-urilor în afara condițiilor, buclelor și funcțiilor împliate.

Vom dezvolta o aplicație mobilă la cheie

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

Discutați proiectul

Citiți și