Kako ispravno koristiti useEffect i useState u Reactu

Zadnje ažuriranje: 02/12/2026
  • Razumjeti kako useState čuva i ažurira stanje lokalnih komponenti, uključujući funkcionalna ažuriranja i rukovanje objektima.
  • Koristite useEffect za nuspojave s jasnom logikom postavljanja/čišćenja i točnim nizovima ovisnosti kako biste izbjegli curenja i petlje.
  • Kombinirajte useState i useEffect za zadatke iz stvarnog svijeta poput dohvaćanja podataka, pretplata i ažuriranja DOM-a u funkcijskim komponentama.
  • Slijedite pravila hookova i tretirajte efekte kao procese "nakon renderiranja" kako bi React komponente bile predvidljive i održive.

React hooks useState i useEffect

React hooks je potpuno promijenio način na koji pišemo komponente, i mastering useState i useEffect je u osnovi ulaznica za pisanje modernog React koda. Ako ih već koristite, ali se i dalje zaglavite s beskonačnim petljama, zastarjelim stanjem ili zbunjujućim nizovima ovisnosti, ovaj vodič će vam pomoći da na praktičan način povežete sve nedostajuće dijelove.

U ovom članku ćemo detaljno objasniti kako ga pravilno koristiti useState i useEffect zajedno, zašto su uopće uvedene kuke, službena pravila i upozorenja, kako ovisnosti zapravo funkcioniraju "ispod haube", uobičajene zamke koje oštećuju vaše komponente i provjereni obrasci za nuspojave, čišćenje i upravljanje stanjem u stvarnim projektima.

Zašto hookovi i zašto posebno useState i useEffect?

U Reactu 16.8 dodani su hookovi kako bi funkcijske komponente mogle koristiti značajke stanja i životnog ciklusa bez klasa.Prije toga, morali ste pisati komponente klase kako biste zadržali lokalno stanje, pretplatili se na vanjske podatke ili reagirali na događaje životnog ciklusa poput montiranja i demontiranja.

Veliki problem s klasama bio je taj što je povezana logika često bila podijeljena na više metoda životnog ciklusa poput componentDidMount, componentDidUpdate i componentWillUnmountZavršili biste s dijelovima iste značajke raspršenim po različitim metodama temeljenim na kada trče umjesto što to rade, što otežava čitanje, testiranje i ponovnu upotrebu koda.

Kuke okreću ovaj model: sa useState stanje izravno pridružujete funkcijskoj komponenti i s useEffect Nuspojave izravno pridružujete logici kojoj su potrebne. Na taj način možete grupirati sve što je povezano s jednim problemom na jednom mjestu i kasnije lako izdvojiti kuke za višekratnu upotrebu.

Među svim udicama, useState i useEffect su osnovni primitiviVećinu svakodnevnih značajki možete izgraditi samo s ova dva: UI stanje poput obrazaca i prekidača, mrežni zahtjevi, pretplate, timeri, ažuriranja DOM-a i još mnogo toga. Ostali hookovi (useRef, useReducer, useContext, useMemo...) su odlični, ali se nadograđuju na iste ideje.

Pravila React hookova koja nikada ne smijete prekršiti

React hookovi dolaze s nekoliko strogih pravila koja im omogućuju pouzdan rad na svim renderima.Ako ih prekršite, vidjet ćete ili pogreške tijekom izvođenja ili vrlo suptilne greške koje je teško otkloniti.

Prvo pravilo: pozivajte hooks samo unutar React funkcionalnih komponenti ili prilagođenih hooks-ovaNe možete koristiti useState or useEffect u komponentama klase, regularnim korisnim funkcijama ili izvan bilo koje komponente. Uzorak poput ovog nije valjan:

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

class App extends Component {
  // ❌ This will throw - hooks don’t work in classes
  const  = useState(0);
  render() {
    return <h1>Hello, I am a Class Component!</h1>;
  }
}

Ispravan pristup je prelazak na funkcijsku komponentu ako želite koristiti hooks-ove.:

import React, { useState } from 'react';

function App() {
  const  = useState('');

  return (
    <div>
      Your JSX code goes in here...
    </div>
  );
}

export default App;

Drugo pravilo: pozivajte hooks samo na najvišoj razini vaše komponenteTo znači da nema hook-ova unutar petlji, uvjeta ili ugniježđenih funkcija. React se oslanja na pozivanje hook-ova istim redoslijedom pri svakom renderiranju kako bi se svaki "uskladio". useState i useEffect poziv sa svojim pohranjenim podacima, tako da je ovo nevažeće:

function BadComponent({ enabled }) {
  if (enabled) {
    // ❌ Wrong: hook inside a conditional
    const  = useState(0);
  }
  // ...
}

Umjesto toga, bezuvjetno deklarirajte hooks na vrhu i koristite uvjetne izraze unutar efekta ili JSX-aHook se uvijek mora pozvati, ali logika koju izvršava može biti uvjetna:

function ConditionalEffectComponent() {
  const  = useState(false);

  useEffect(() => {
    if (isMounted) {
      console.log('Component mounted');
    }
  }, );

  return (
    <div>
      <button onClick={() => setIsMounted(!isMounted)}>
        {isMounted ? 'Unmount' : 'Mount'}
      </button>
    </div>
  );
}

Treće implicitno pravilo je da se hookovi moraju uvoziti iz Reacta (ili biblioteke hookova), a ne implementirati ad-hoc.Ovo je očito, ali vrijedi spomenuti: magija je u Reactovom internom dispečeru hook-ova koji prati pozive hook-ova kroz rendere.

Ispravno upravljanje lokalnim stanjem pomoću useState

useState omogućuje vam prilaganje stanja funkcijskoj komponenti i primanje i trenutne vrijednosti i funkcije ažuriranjaKonceptualno, to je funkcionalni pandan od this.state i this.setState u komponentama klase.

Minimalni primjer protumjere s useState izgleda ovako:

import React, { useState } from 'react';

function Counter() {
  const  = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

export default Counter;

Kad nazovete useState(initialValue), React pohranjuje to stanje i vraća par: trenutna vrijednost stanja i setter. Za razliku od normalnih lokalnih varijabli, stanje opstaje kroz renderiranja, tako da count Vrijednost se ne resetira na 0 svaki put kada se funkcija komponente pokrene.

Za stanje možete koristiti bilo koju serijalizirajuću vrijednost: brojeve, stringove, logičke vrijednosti, nizove, objekte, pa čak i funkcije.. Možete i nazvati useState više puta u istoj komponenti kako bi se povezane vrijednosti držale odvojeno umjesto da se sve strpa u jedan objekt.

Kada nova vrijednost stanja ovisi o prethodnoj, uvijek koristite obrazac za funkcionalno ažuriranjeTime se izbjegavaju greške kada se više ažuriranja stanja dogodi u brzom slijedu:

setCount(prev => prev + 1);

Još jedan suptilan, ali važan detalj je da pozivanje settera zamjenjuje cijelu vrijednost stanja, ne spaja objekte poput this.setState u razredimaAko je vaše stanje objekt ili niz, morate sami proširiti prethodnu vrijednost:

const  = useState({ name: 'Alex', age: 30 });

// ✅ Correct: copy and update
setUser(prev => ({ ...prev, age: prev.age + 1 }));

Za skupe početne vrijednosti možete lijeno inicijalizirati stanje prosljeđivanjem funkcije useStateReact će ga pozvati samo pri prvom renderiranju:

const  = useState(() => calculateInitialValue());

Rješavanje nuspojava s useEffectom

useEffect je React-ov API za pokretanje nuspojava u funkcijskim komponentama„Nuspojava“ je sve što dodiruje vanjski svijet: dohvaćanje podataka, zapisivanje, izravne promjene DOM-a, pretplate, mjerači vremena, API-ji preglednika itd.

konceptualno, useEffect zamjenjuje kombinaciju componentDidMount, componentDidUpdate i componentWillUnmount iz komponenti klaseUmjesto podjele jednog efekta na tri metode životnog ciklusa, deklarirate ga jednom i pustite da React obrađuje kada se izvršava i kada čisti.

Osnovni potpis je useEffect(setup, dependencies?), setup funkcija je tijelo vašeg efekta; opcionalno može vratiti funkciju čišćenja. dependencies niz govori Reactu kada efekt treba ponovno pokrenuti.

useEffect(() => {
  // side effect logic here

  return () => {
    // optional cleanup logic here
  };
}, );

Prema zadanim postavkama, bez drugog argumenta, efekt će se izvršavati nakon svakog renderiranja. (prvo montiranje i svako sljedeće ažuriranje). To je često previše za mrežne zahtjeve ili skupu logiku.

Vrlo čest obrazac je ažuriranje nečeg vanjskog kad god se promijeni dio stanjaNa primjer, ažuriranje naslova stranice ovisno o broju klikova:

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

function Counter() {
  const  = useState(0);

  useEffect(() => {
    document.title = `You clicked ${count} times`;
  }, ); // effect re-runs only when `count` changes

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

Niz ovisnosti ključan je za performanse i ispravnostKontrolira kada React treba ponovno pokrenuti efekt: ako se neka ovisnost promijenila u skladu s Object.is usporedbe, efekt se čisti i ponovno pokreće; ako se ništa nije promijenilo, preskače se.

Razumijevanje niza ovisnosti kao profesionalac

Niz ovisnosti je najsuptilniji useEffect kukci dolaze izReact uspoređuje svaki element niza s njegovom prethodnom vrijednošću koristeći Object.isAko su sve vrijednosti jednake, efekt se preskače; ako je barem jedna drugačija, efekt se ponovno izvršava.

Postoje tri glavne konfiguracije ovisnosti koje ćete stalno koristiti:

  • Nema drugog argumenta: efekt se izvršava nakon svakog renderiranja.
  • Prazan niz []: efekt se izvršava samo jednom pri montiranju i nestaje pri odmontiranju.
  • Niz s vrijednostima : efekt se izvršava nakon montiranja i kad god se promijeni bilo koja ovisnost.

Kada su zavisnosti primitivne vrijednosti (brojevi, stringovi, logičke vrijednosti), ovo je jednostavnoProblemi počinju kada stavite objekte, nizove ili funkcije unutar ovisnosti, jer je jednakost temeljena na referencama. Dva identična objekta s različitim referencama smatraju se "različitima", što uzrokuje ponovna pokretanja pri svakom renderiranju.

Razmotrimo učinak koji ovisi o team predmet iz rekvizita:

function Team({ team }) {
  useEffect(() => {
    console.log(team.id, team.active);
  }, ); // ⚠️ might re-run every render if `team` reference changes
}

Čak i ako se stvarni sadržaj tima ne promijeni, nova referenca objekta pri svakom renderiranju prisilit će efekt da se ponovno pokrene.Da biste to izbjegli, oslonite se na primitivna polja koja zapravo koristite ili rekonstruirajte objekt unutar samog efekta.

Sigurnija verzija prati samo ono što je stvarno potrebno za učinak:

function Team({ team }) {
  const { id, active } = team;

  useEffect(() => {
    console.log(id, active);
  }, );
}

Ako vam je zaista potreban cijeli objekt unutar efekta, možete ga tamo ponovno stvoriti umjesto da ga koristite kao ovisnost.Na taj način popis ovisnosti i dalje može biti zasnovan na primitivima:

function Team({ team }) {
  const { id, active, name } = team;

  useEffect(() => {
    const localTeam = { id, active, name };
    // use `localTeam` here
  }, );
}

Kao krajnje sredstvo možete koristiti memoizaciju s useMemo or useCallback za skupe predmete ili funkcije, ali imajte na umu da sama memoizacija ima svoju cijenu. Nemojte je posipati posvuda "za svaki slučaj"; dodajte je kada određena ovisnost stvarno uzrokuje probleme s performansama.

Pravilno čišćenje efekata

Neke nuspojave dodjeljuju resurse koji se moraju osloboditi: pretplate, utičnice, intervali, vremenska ograničenja, slušači događaja itd. Zaboravljanje njihovog čišćenja može lako dovesti do curenja memorije ili dupliciranog rada.

In useEffect, čišćenje se obavlja vraćanjem funkcije iz efektaReact će pozvati ovu funkciju prije ponovnog pokretanja efekta s novim ovisnostima, a također i posljednji put kada se komponenta odmontira.

import { useEffect } from 'react';

function LogMessage({ message }) {
  useEffect(() => {
    const log = setInterval(() => {
      console.log(message);
    }, 1000);

    return () => {
      clearInterval(log);
    };
  }, );

  return <div>logging to console "{message}"</div>;
}

U ovom primjeru, svaki put message promjene, React prvo briše stari interval, a zatim postavlja novi s ažuriranom porukomKada komponenta nestane iz korisničkog sučelja, posljednje čišćenje trajno briše interval.

Ovaj par „postavljanje + čišćenje“ ključan je za mentalni model useEffectPokušajte zamisliti svaki efekt kao samostalni proces koji počinje u funkciji postavljanja i potpuno se zaustavlja u funkciji čišćenja. React može pokrenuti više ciklusa postavljanja/čišćenja u razvoju (posebno u Strogom načinu rada) kako bi testirao da vaše čišćenje doista sve poništava.

Klasičan primjer je pretplata na vanjski izvor, poput API-ja za chat ili događaja preglednika (vidi rukovanje onKeyDown u Reactu):

useEffect(() => {
  function handleClick(event) {
    console.log('Clicked', event.clientX, event.clientY);
  }

  document.addEventListener('click', handleClick);

  return () => {
    document.removeEventListener('click', handleClick);
  };
}, []); // runs once on mount, cleans up on unmount

Zajedničko korištenje useState i useEffect za dohvaćanje podataka

Jedna od najčešćih kombinacija u stvarnom svijetu je korištenje useState i useEffect za dohvaćanje podataka iz API-jaPodatke (i možda zastavice učitavanja/pogreške) zadržavate u stanju i izvršavate zahtjev na način koji se pokreće kada se komponenta montira ili kada se neki parametar promijeni.

Osnovni obrazac za dohvaćanje podataka nakon montiranja izgleda ovako:

import { useEffect, useState } from 'react';

function FetchItems() {
  const  = useState([]);

  useEffect(() => {
    let ignore = false;

    async function fetchItems() {
      try {
        const response = await fetch('/items');
        const fetchedItems = await response.json();
        if (!ignore) {
          setItems(fetchedItems);
        }
      } catch (error) {
        console.error('Error fetching items:', error);
      }
    }

    fetchItems();

    return () => {
      // avoid updating state if the component unmounted
      ignore = true;
    };
  }, []);

  return (
    <div>
      {items.map(item => (
        <div key={item.id ?? item}>{item.name ?? item}</div>
      ))}
    </div>
  );
}

Ovdje, prazan niz ovisnosti osigurava da se zahtjev izvršava točno jednomUnutarnji ignore Zastavica je jednostavan način izbjegavanja postavljanja stanja na nemontiranu komponentu u slučaju da se zahtjev riješi kasno.

Također je vrlo uobičajeno dodati zastavicu učitavanja i prikazati rotirajući gumb ili rezervirano mjesto dok su podaci u tijeku.:

const Statistics = () => {
  const  = useState([]);
  const  = useState(true);

  useEffect(() => {
    const getStats = async () => {
      try {
        const statsData = await getData();
        setStats(statsData);
      } finally {
        setLoading(false);
      }
    };

    getStats();
  }, []);

  if (loading) {
    return <div>Loading statistics...</div>;
  }

  return (
    <ul>
      {stats.map(stat => (
        <li key={stat.id}>{stat.label}: {stat.value}</li>
      ))}
    </ul>
  );
};

Ako vaš upit ovisi o parametru (kao što je kategorija, filter ili parametar rute), dodajte taj parametar u niz ovisnosti pa se efekt ponavlja kada se promijeni:

useEffect(() => {
  async function fetchItems() {
    const response = await fetch(`/items?category=${category}`);
    const data = await response.json();
    setItems(data);
  }

  fetchItems();
}, );

Razmišljanje u smislu "efekti na svakom renderiranju" u odnosu na "životne cikluse"

Ako ste navikli na komponente klase, može biti primamljivo mentalno mapirati useEffect za montiranje/ažuriranje/demontiranje metoda, ali to obično dovodi do veće zbrke. Jednostavniji mentalni model je: „efekti se izvode nakon renderiranja i mogu se očistiti prije sljedećeg pokretanja“.

U nastavi ste često morali duplicirati logiku između componentDidMount i componentDidUpdate jer ste htjeli da se isti efekt izvršava i pri montiranju i pri ažuriranjima. S hookovima to dupliciranje nestaje: jedan efekt pokriva oba slučaja, a React se brine za čišćenje između izvršavanja.

Ovaj dizajn također eliminira cijelu klasu grešaka vezanih uz neispravno rukovanje ažuriranjima.Na primjer, u komponenti razreda koja se pretplaćuje na online status prijatelja, lako je zaboraviti ponovno se pretplatiti kada props.friend promjene, što uzrokuje zastarjele pretplate ili rušenja prilikom odspajanja. S useEffect koji navodi friend.id Kao ovisnost, React će automatski pokrenuti čišćenje za starog prijatelja i postavljanje za novog.

Imajte na umu da u Strict Modeu razvoja, React namjerno pokreće ciklus postavljanja + čišćenja dva puta pri montiranju.To se ne događa u produkciji, ali je koristan test opterećenja kako biste potvrdili da vaše čišćenje doista sve poništava i da se vaš efekt može sigurno izvoditi više puta.

Optimizacija i rješavanje problema ponašanja useEffecta

Kada se efekt izvršava češće nego što očekujete, prvo što treba provjeriti je niz ovisnosti.Ili se ovisnost mijenja pri svakom renderiranju (uobičajeno kod inline objekata/funkcija) ili ste uopće zaboravili navesti niz.

Zapisivanje vrijednosti ovisnosti je brz način za otklanjanje pogrešaka:

useEffect(() => {
  console.log('Effect deps:', dep1, dep2);
}, );

Ako svaki put vidite različite zapise, provjerite koja se ovisnost zapravo mijenjaČesto ćete pronaći ugrađeni objekt ili funkciju strelice koja se ponovno stvara pri svakom renderiranju. Premještanje stvaranja objekta unutar efekta, podizanje funkcija izvan komponente ili njihovo memoiziranje pomoću useCallback može stabilizirati ovisnosti kada je to potrebno.

Beskonačne petlje se događaju kada efekt ovisi o vrijednosti i bezuvjetno ažurira tu istu vrijednost., Na primjer:

useEffect(() => {
  setCount(count + 1); // ⚠️ will cause a loop if `count` is a dependency
}, );

Svaki put count promjene, efekt se izvodi, ažuriranja count ponovno pokreće još jedno renderiranje i tako daljeKako biste prekinuli taj obrazac, razmislite pripada li ažuriranje stanja doista efektu, treba li ga pokrenuti interakcija korisnika ili se možete osloniti na drugu vrijednost.

Ponekad želite pročitati najnoviju vrijednost nekog stanja ili svojstava unutar efekta bez da ta vrijednost pokrene ponovno pokretanje.U tim naprednim scenarijima, noviji API-ji poput „efektnih događaja“ (putem useEffectEvent u React dokumentaciji) ili reference mogu pomoći, ali za većinu praktičnih slučajeva, ostati vjeran ovisnostima je sigurnije i jednostavnije.

Spajanje svega, korištenje useState i useEffect ispravno se svodi na nekoliko osnovnih navika: održavajte stanje malim i fokusiranim, preferirajte funkcionalna ažuriranja prilikom izvođenja novog stanja iz starog, strukturirajte efekte oko parova postavljanja/čišćenja, budite iskreni i eksplicitni s nizovima ovisnosti i uvijek poštujte pravila hook-ova kako bi React mogao pouzdano pratiti što gdje pripada. Kada slijedite te principe, vaše komponente ostaju predvidljive, vaši nuspojave se ponašaju, a vaša React kodna baza postaje puno lakša za razvoj kako vaša aplikacija raste.

Povezani članak:
Riješeno: Kako instalirati react izvorne kuke s
Povezani postovi: