Hrvatski

Sveobuhvatan vodič za upravljanje stanjem u Reactu za globalnu publiku. Istražite useState, Context API, useReducer i popularne biblioteke kao što su Redux, Zustand i TanStack Query.

Ovladavanje upravljanjem stanjem u Reactu: Globalni vodič za developere

U svijetu front-end razvoja, upravljanje stanjem jedan je od najkritičnijih izazova. Za developere koji koriste React, ovaj se izazov razvio od jednostavne brige na razini komponente do složene arhitektonske odluke koja može definirati skalabilnost, performanse i održivost aplikacije. Bilo da ste samostalni developer u Singapuru, dio distribuiranog tima diljem Europe ili osnivač startupa u Brazilu, razumijevanje krajolika upravljanja stanjem u Reactu ključno je za izgradnju robusnih i profesionalnih aplikacija.

Ovaj sveobuhvatni vodič provest će vas kroz cijeli spektar upravljanja stanjem u Reactu, od njegovih ugrađenih alata do moćnih vanjskih biblioteka. Istražit ćemo 'zašto' iza svakog pristupa, pružiti praktične primjere koda i ponuditi okvir za donošenje odluka koji će vam pomoći odabrati pravi alat za vaš projekt, bez obzira gdje se nalazite u svijetu.

Što je 'stanje' (state) u Reactu i zašto je toliko važno?

Prije nego što zaronimo u alate, uspostavimo jasno, univerzalno razumijevanje 'stanja'. U suštini, stanje je bilo koji podatak koji opisuje stanje vaše aplikacije u određenom trenutku. To može biti bilo što:

React se temelji na principu da je korisničko sučelje (UI) funkcija stanja (UI = f(stanje)). Kada se stanje promijeni, React učinkovito ponovno iscrtava (re-renderira) potrebne dijelove korisničkog sučelja kako bi odrazio tu promjenu. Izazov nastaje kada to stanje treba dijeliti i mijenjati više komponenti koje nisu izravno povezane u stablu komponenti. Tu upravljanje stanjem postaje ključna arhitektonska briga.

Temelj: Lokalno stanje s useState

Putovanje svakog React developera započinje s useState hookom. To je najjednostavniji način za deklariranje dijela stanja koje je lokalno za jednu komponentu.

Na primjer, upravljanje stanjem jednostavnog brojača:


import React, { useState } from 'react';

function Counter() {
  // 'count' je varijabla stanja
  // 'setCount' je funkcija za njezino ažuriranje
  const [count, setCount] = useState(0);

  return (
    

Kliknuli ste {count} puta

); }

useState je savršen za stanje koje se ne treba dijeliti, kao što su unosi u obrascima, preklopnici ili bilo koji element korisničkog sučelja čije stanje ne utječe na druge dijelove aplikacije. Problem počinje kada trebate da neka druga komponenta zna vrijednost `count`.

Klasični pristup: Podizanje stanja (Lifting State Up) i 'Prop Drilling'

Tradicionalni način dijeljenja stanja između komponenti u Reactu je "podići ga" do njihovog najbližeg zajedničkog pretka. Stanje se zatim spušta do podređenih komponenti putem propsa. Ovo je temeljni i važan React uzorak.

Međutim, kako aplikacije rastu, to može dovesti do problema poznatog kao "prop drilling". To se događa kada morate prosljeđivati propse kroz više slojeva posredničkih komponenti koje zapravo ne trebaju te podatke, samo da bi ih dostavile duboko ugniježđenoj podređenoj komponenti koja ih treba. To može učiniti kod težim za čitanje, refaktoriranje i održavanje.

Zamislite korisnikovu postavku teme (npr. 'tamna' ili 'svijetla') kojoj treba pristupiti gumb duboko unutar stabla komponenti. Možda ćete je morati proslijediti ovako: App -> Layout -> Page -> Header -> ThemeToggleButton. Samo `App` (gdje je stanje definirano) i `ThemeToggleButton` (gdje se koristi) brinu o ovom propsu, ali `Layout`, `Page` i `Header` prisiljeni su djelovati kao posrednici. To je problem koji naprednija rješenja za upravljanje stanjem nastoje riješiti.

Reactova ugrađena rješenja: Moć Contexta i Reducera

Prepoznavši izazov 'prop drillinga', React tim je uveo Context API i `useReducer` hook. To su moćni, ugrađeni alati koji mogu riješiti značajan broj scenarija upravljanja stanjem bez dodavanja vanjskih ovisnosti.

1. Context API: Globalno emitiranje stanja

Context API pruža način za prosljeđivanje podataka kroz stablo komponenti bez potrebe za ručnim prosljeđivanjem propsa na svakoj razini. Zamislite ga kao globalno spremište podataka za određeni dio vaše aplikacije.

Korištenje Contexta uključuje tri glavna koraka:

  1. Kreiranje Contexta: Koristite `React.createContext()` za stvaranje context objekta.
  2. Pružanje Contexta: Koristite komponentu `Context.Provider` da omotate dio vašeg stabla komponenti i proslijedite joj `value`. Bilo koja komponenta unutar ovog providera može pristupiti toj vrijednosti.
  3. Korištenje Contexta: Koristite `useContext` hook unutar komponente kako biste se pretplatili na context i dobili njegovu trenutnu vrijednost.

Primjer: Jednostavan preklopnik tema pomoću Contexta


// 1. Kreirajte Context (npr. u datoteci theme-context.js)
import { createContext, useState } from 'react';

export const ThemeContext = createContext();

export function ThemeProvider({ children }) {
  const [theme, setTheme] = useState('light');

  const toggleTheme = () => {
    setTheme(prevTheme => (prevTheme === 'light' ? 'dark' : 'light'));
  };

  // Objekt 'value' bit će dostupan svim komponentama koje ga koriste
  const value = { theme, toggleTheme };

  return (
    
      {children}
    
  );
}

// 2. Pružite Context (npr. u vašoj glavnoj App.js datoteci)
import { ThemeProvider } from './theme-context';
import MyPage from './MyPage';

function App() {
  return (
    
      
    
  );
}

// 3. Koristite Context (npr. u duboko ugniježđenoj komponenti)
import { useContext } from 'react';
import { ThemeContext } from './theme-context';

function ThemeToggleButton() {
  const { theme, toggleTheme } = useContext(ThemeContext);

  return (
    
  );
}

Prednosti Context API-ja:

Nedostaci i razmatranja o performansama:

2. `useReducer` Hook: Za predvidljive tranzicije stanja

Dok je `useState` odličan za jednostavno stanje, `useReducer` je njegov moćniji srodnik, dizajniran za upravljanje složenijom logikom stanja. Posebno je koristan kada imate stanje koje uključuje više pod-vrijednosti ili kada sljedeće stanje ovisi o prethodnom.

Inspiriran Reduxom, `useReducer` uključuje `reducer` funkciju i `dispatch` funkciju:

Primjer: Brojač s akcijama za povećanje, smanjenje i resetiranje


import React, { useReducer } from 'react';

// 1. Definirajte početno stanje
const initialState = { count: 0 };

// 2. Kreirajte reducer funkciju
function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    case 'reset':
      return initialState;
    default:
      throw new Error('Neočekivani tip akcije');
  }
}

function ReducerCounter() {
  // 3. Inicijalizirajte useReducer
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <>
      

Broj: {state.count}

{/* 4. Šaljite akcije (dispatch) na interakciju korisnika */} ); }

Korištenje `useReducer` centralizira vašu logiku ažuriranja stanja na jednom mjestu (reducer funkciji), čineći je predvidljivijom, lakšom za testiranje i održivijom, pogotovo kako logika raste u složenosti.

Moćni par: `useContext` + `useReducer`

Prava snaga Reactovih ugrađenih hookova ostvaruje se kada kombinirate `useContext` i `useReducer`. Ovaj uzorak omogućuje vam stvaranje robusnog rješenja za upravljanje stanjem nalik Reduxu bez ikakvih vanjskih ovisnosti.

Ovaj je uzorak fantastičan jer sama `dispatch` funkcija ima stabilan identitet i neće se mijenjati između ponovnih iscrtavanja. To znači da se komponente koje samo trebaju `dispatchati` akcije neće nepotrebno ponovno iscrtavati kada se vrijednost stanja promijeni, pružajući ugrađenu optimizaciju performansi.

Primjer: Upravljanje jednostavnom košaricom za kupnju


// 1. Postavljanje u cart-context.js
import { createContext, useReducer, useContext } from 'react';

const CartStateContext = createContext();
const CartDispatchContext = createContext();

const cartReducer = (state, action) => {
  switch (action.type) {
    case 'ADD_ITEM':
      // Logika za dodavanje stavke
      return [...state, action.payload];
    case 'REMOVE_ITEM':
      // Logika za uklanjanje stavke po ID-u
      return state.filter(item => item.id !== action.payload.id);
    default:
      throw new Error(`Nepoznata akcija: ${action.type}`);
  }
};

export const CartProvider = ({ children }) => {
  const [state, dispatch] = useReducer(cartReducer, []);

  return (
    
      
        {children}
      
    
  );
};

// Prilagođeni hookovi za jednostavnu upotrebu
export const useCart = () => useContext(CartStateContext);
export const useCartDispatch = () => useContext(CartDispatchContext);

// 2. Korištenje u komponentama
// ProductComponent.js - treba samo slati akciju
function ProductComponent({ product }) {
  const dispatch = useCartDispatch();
  
  const handleAddToCart = () => {
    dispatch({ type: 'ADD_ITEM', payload: product });
  };

  return ;
}

// CartDisplayComponent.js - treba samo čitati stanje
function CartDisplayComponent() {
  const cartItems = useCart();

  return 
Stavke u košarici: {cartItems.length}
; }

Razdvajanjem stanja i dispatcha u dva odvojena contexta, dobivamo prednost u performansama: komponente poput `ProductComponent` koje samo šalju akcije neće se ponovno iscrtavati kada se stanje košarice promijeni.

Kada posegnuti za vanjskim bibliotekama

Uzorak `useContext` + `useReducer` je moćan, ali nije univerzalno rješenje. Kako aplikacije rastu, mogli biste se susresti s potrebama koje su bolje zadovoljene pomoću namjenskih vanjskih biblioteka. Trebali biste razmotriti vanjsku biblioteku kada:

Globalni pregled popularnih biblioteka za upravljanje stanjem

React ekosustav je živahan i nudi širok spektar rješenja za upravljanje stanjem, svako sa svojom filozofijom i kompromisima. Istražimo neke od najpopularnijih izbora za developere diljem svijeta.

1. Redux (& Redux Toolkit): Uspostavljeni standard

Redux je godinama bio dominantna biblioteka za upravljanje stanjem. On nameće strogi jednosmjerni protok podataka, čineći promjene stanja predvidljivima i sljedivima. Dok je rani Redux bio poznat po svom 'boilerplateu' (ponavljajućem kodu), moderni pristup koji koristi Redux Toolkit (RTK) značajno je pojednostavio proces.

2. Zustand: Minimalistički i neopterećen izbor

Zustand, što na njemačkom znači "stanje", nudi minimalistički i fleksibilan pristup. Često se smatra jednostavnijom alternativom Reduxu, pružajući prednosti centraliziranog storea bez boilerplatea.


// store.js
import { create } from 'zustand';

const useBearStore = create((set) => ({
  bears: 0,
  increasePopulation: () => set((state) => ({ bears: state.bears + 1 })),
  removeAllBears: () => set({ bears: 0 }),
}));

// MyComponent.js
function BearCounter() {
  const bears = useBearStore((state) => state.bears);
  return 

{bears} medvjeda je ovdje ...

; } function Controls() { const increasePopulation = useBearStore((state) => state.increasePopulation); return ; }

3. Jotai & Recoil: Atomski pristup

Jotai i Recoil (iz Facebooka) populariziraju koncept "atomskog" upravljanja stanjem. Umjesto jednog velikog objekta stanja, razbijate svoje stanje na male, neovisne dijelove zvane "atomi".

4. TanStack Query (ranije React Query): Kralj stanja s poslužitelja

Možda najznačajnija promjena paradigme posljednjih godina je spoznaja da je velik dio onoga što nazivamo "stanjem" zapravo stanje s poslužitelja — podaci koji žive na poslužitelju, a dohvaćaju se, keširaju i sinkroniziraju u našoj klijentskoj aplikaciji. TanStack Query nije generički upravitelj stanjem; to je specijalizirani alat za upravljanje stanjem s poslužitelja, i to radi izvanredno dobro.

Donošenje prave odluke: Okvir za odlučivanje

Odabir rješenja za upravljanje stanjem može se činiti neodoljivim. Evo praktičnog, globalno primjenjivog okvira za odlučivanje koji će vas voditi. Postavite si ova pitanja redom:

  1. Je li stanje doista globalno ili može biti lokalno?
    Uvijek počnite s useState. Ne uvodite globalno stanje osim ako je apsolutno nužno.
  2. Jesu li podaci kojima upravljate zapravo stanje s poslužitelja?
    Ako su to podaci s API-ja, koristite TanStack Query. On će se pobrinuti za keširanje, dohvaćanje i sinkronizaciju za vas. Vjerojatno će upravljati s 80% "stanja" vaše aplikacije.
  3. Za preostalo UI stanje, trebate li samo izbjeći 'prop drilling'?
    Ako se stanje ažurira rijetko (npr. tema, informacije o korisniku, jezik), ugrađeni Context API je savršeno rješenje bez ovisnosti.
  4. Je li vaša logika UI stanja složena, s predvidljivim tranzicijama?
    Kombinirajte useReducer s Contextom. To vam daje moćan, organiziran način upravljanja logikom stanja bez vanjskih biblioteka.
  5. Imate li problema s performansama s Contextom, ili se vaše stanje sastoji od mnogo neovisnih dijelova?
    Razmislite o atomskom upravitelju stanjem poput Jotaia. Nudi jednostavan API s izvrsnim performansama sprječavanjem nepotrebnih ponovnih iscrtavanja.
  6. Gradite li veliku poslovnu aplikaciju koja zahtijeva strogu, predvidljivu arhitekturu, middleware i moćne alate za debugiranje?
    Ovo je glavni slučaj upotrebe za Redux Toolkit. Njegova struktura i ekosustav dizajnirani su za složenost i dugoročnu održivost u velikim timovima.

Sažeta usporedna tablica

Rješenje Najbolje za Ključna prednost Krivulja učenja
useState Lokalno stanje komponente Jednostavno, ugrađeno Vrlo niska
Context API Globalno stanje niske frekvencije (tema, auth) Rješava 'prop drilling', ugrađeno Niska
useReducer + Context Složeno UI stanje bez vanjskih biblioteka Organizirana logika, ugrađeno Srednja
TanStack Query Stanje s poslužitelja (API keširanje/sinkronizacija) Eliminira ogromne količine logike stanja Srednja
Zustand / Jotai Jednostavno globalno stanje, optimizacija performansi Minimalni boilerplate, odlične performanse Niska
Redux Toolkit Velike aplikacije sa složenim, dijeljenim stanjem Predvidljivost, moćni alati za developere, ekosustav Visoka

Zaključak: Pragmatična i globalna perspektiva

Svijet upravljanja stanjem u Reactu više nije bitka jedne biblioteke protiv druge. Sazrio je u sofisticirani krajolik gdje su različiti alati dizajnirani za rješavanje različitih problema. Moderni, pragmatični pristup je razumjeti kompromise i izgraditi 'set alata za upravljanje stanjem' za svoju aplikaciju.

Za većinu projekata diljem svijeta, moćan i učinkovit 'stack' počinje s:

  1. TanStack Query za svo stanje s poslužitelja.
  2. useState za svo nedijeljeno, jednostavno UI stanje.
  3. useContext za jednostavno, globalno UI stanje niske frekvencije.

Tek kada su ti alati nedovoljni, trebali biste posegnuti za namjenskom globalnom bibliotekom za stanje kao što su Jotai, Zustand ili Redux Toolkit. Jasnim razlikovanjem između stanja poslužitelja i stanja klijenta, te započinjanjem s najjednostavnijim rješenjem, možete izgraditi aplikacije koje su performantne, skalabilne i ugodne za održavanje, bez obzira na veličinu vašeg tima ili lokaciju vaših korisnika.