# Zašto rukovanje greškama u Reactu treba slojeviti sustav u 2026.#
Moderni React sustavi su kompozabilni, streaming i izrazito asinkroni. To znači da se greške mogu dogoditi u više ravnina: renderiranje, routing, mreža, dozvole i ovisnosti trećih strana.
Pristup koji se dobro skalira tretira rukovanje greškama kao slojevitu obranu, a ne kao jedan “catch-all”. Timovi koji se oslanjaju na jedan globalni toast i jedan generički fallback često isporučuju krhak UX i stvaraju slijepe točke u opservabilnosti.
Ovaj vodič se fokusira na obrasce rukovanja greškama u Reactu koji se mogu skalirati kroz timove i proizvode razdvajanjem odgovornosti:
- Granice na razini komponente za UI kvarove
- Granice na razini rute za izolaciju na razini stranice
- Ponovni pokušaji dohvata podataka za prolazne mrežne probleme
- Poruke korisnicima koje nude jasan put oporavka
Dobit ćete i ponovno upotrebljive obrasce i anti-patterns za TanStack Query i SWR, uključujući politike ponovnih pokušaja i ponašanje mutacija.
# Slojeviti model: gdje treba hvatati greške#
Pouzdan sustav odgovara na dva pitanja za svaki kvar.
- 1Gdje treba ograničiti grešku kako bi aplikacija ostala upotrebljiva
- 2Gdje korisniku treba ponuditi smislen način oporavka
Koristite ovu slojevitu mapu kao zadani okvir.
| Sloj | Hvata | Tipični simptomi | Najbolji UI odgovor |
|---|---|---|---|
| Granica komponente | Greške renderiranja u widgetu ili značajci | Pokvarena kartica, graf, editor | Inline fallback za tu komponentu |
| Granica rute | Greške stranice ili segmenta rute | Puca cijela stranica | Stranica greške na razini rute s retry i navigacijom |
| Ponovni pokušaji u podatkovnom sloju | Prolazne mrežne greške i 5xx | Spinneri zapnu, povremeni padovi | Automatski retry pa inline error stanje |
| Poruke korisnicima | Ishodi koji presijecaju aplikaciju | Spremanje nije uspjelo, sesija je istekla | Toast za globalne događaje, inline za polja forme |
🎯 Ključna poruka: Cilj nije spriječiti greške. Cilj je ograničiti “blast radius”, omogućiti oporavak i držati korisnike u pokretu.
Ako gradite s Next.js App Routerom, uskladite ovaj model s granicama grešaka po segmentima rute i loading stanjima. Ovo se dobro uklapa s obrascima u Next.js App Router: granice grešaka, loading i streaming obrasci.
# Component Error Boundaries koje ne uništavaju stranicu#
Error boundaries su i dalje osnovni React primitiv za izolaciju UI kvarova. U 2026. standard je postavljati granice oko feature widgeta, a ne oko cijele aplikacije.
Što Error Boundaries hvataju, a što ne#
Error boundaries hvataju:
- Greške tijekom renderiranja
- Greške u konstruktorima
- Greške u lifecycle metodama class komponenti
Ne hvataju:
- Greške u event handlerima
- Async greške u promiseovima ili
fetch - Greške unutar
setTimeoutcallbackova - Greške na serveru osim ako ih ponovno ne bacite u render na klijentu
Zato vam treba strategija u podatkovnom sloju, ne samo granice.
Ponovno upotrebljiva Error Boundary komponenta#
Neka granica bude mala i dosljedna, a fallback UI neka korisniku nudi akciju.
import React from "react";
type Props = {
name: string;
fallback: (args: { error: Error; reset: () => void }) => React.ReactNode;
children: React.ReactNode;
onError?: (error: Error) => void;
};
type State = { error: Error | null };
export class ErrorBoundary extends React.Component<Props, State> {
state: State = { error: null };
static getDerivedStateFromError(error: Error) {
return { error };
}
componentDidCatch(error: Error) {
this.props.onError?.(error);
}
reset = () => this.setState({ error: null });
render() {
if (this.state.error) {
return this.props.fallback({ error: this.state.error, reset: this.reset });
}
return this.props.children;
}
}Primjer korištenja za widget s grafom:
<ErrorBoundary
name="RevenueChart"
onError={(e) => console.error("RevenueChart failed", e)}
fallback={({ reset }) => (
<div>
<p>Graf se nije uspio učitati.</p>
<button onClick={reset}>Pokušaj ponovno</button>
</div>
)}
>
<RevenueChart />
</ErrorBoundary>Obrazac: granica po značajci, ne po stranici#
Jedna stranica često sadrži više neovisnih značajki. Ako jedna pukne, ostale trebaju ostati funkcionalne. To je posebno važno u dashboardima i admin alatima.
Praktično pravilo: jedna granica po “widgetu” koji može neovisno otkazati. Ako stranica ima 6 widgeta, vjerojatno želite 6 granica, a ne 1.
💡 Savjet: Granice nazivajte prema značajkama koje korisnik prepoznaje, a ne prema tehničkim komponentama. Logovi postaju pretraživi i korisni kad se naziv poklapa s jezikom proizvoda.
Anti-pattern: hvatanje i ignoriranje u renderu#
Izbjegavajte omatanje render logike u try/catch i vraćanje null. Time skrivate kvar od opservabilnosti i onemogućujete dosljedan recovery UI.
Ako nešto može baciti grešku tijekom renderiranja, pustite da je baci i neka je uhvati granica gdje možete prikazati fallback i logirati.
# Granice rute: zadržite kvarove na razini stranice#
Granice rute rješavaju greške koje sprječavaju da se stranica uopće renderira. U Next.js App Routeru to se tipično mapira na obradu grešaka po segmentu rute. U React Routeru koristite error elemente na razini rute.
Granice rute trebaju dati prioritet:
- Jasnoj poruci da se stranica nije učitala
- Akciji ponovnog pokušaja
- Putanji natrag na sigurnu lokaciju, poput dashboarda ili početne
- Minimalnoj buci, maksimalnoj mogućnosti oporavka
Za što služe granice rute#
Koristite granice rute za:
- Greške autorizacije koje se dogode tijekom učitavanja rute
- Nedostajuće kritične podatke koji blokiraju stranicu
- Neočekivane greške renderiranja koje zahvaćaju većinu stranice
Nemojte koristiti granice rute za:
- Pad jednog widgeta na stranici
- Validaciju na razini polja
- Greške u pozadinskom osvježavanju
UX zahtjevi za route fallback#
Dobar route fallback odgovara na ova pitanja korisnika:
- Mogu li pokušati ponovno
- Jesu li moji podaci sigurni
- Kamo mogu otići umjesto toga
Praktičan predložak:
- Naslov: “Ovu stranicu nije moguće učitati”
- Tijelo: kratko i specifično ako znate uzrok, npr. “Vaša sesija je istekla”
- Gumbi: Retry, Go back, Go to dashboard
- Opcionalno: kod greške za podršku, npr.
ERR_ROUTE_403
# Greške dohvata podataka: ponovni pokušaji koji prate stvarnost#
Većina grešaka koje korisnici vide u React aplikacijama su mrežni ili serverski problemi. Tretiranje svih kvarova kao fatalnih je najbrži put do nepouzdanog UX-a.
U produkcijskoj telemetriji kroz consumer i B2B aplikacije prolazne greške su česte. Stvarne varijacije CDN-ova i ISP-ova znače da treba očekivati sporadične timeoute. Konzervativna pretpostavka dizajna je da će mjerljiv dio zahtjeva povremeno pasti pod opterećenjem ili tijekom deploya.
Vaša strategija:
- Retry za čitanja koja su sigurna za ponavljanje
- Bez retry-a za upise osim ako imate idempotentnost
- Uvijek ograničite broj retry-a i pokažite napredak
- Koristite eksponencijalni backoff s jitterom kako biste izbjegli “retry oluje”
TanStack Query: produkcijski spremna politika retry-a#
TanStack Query daje finu kontrolu retry logike. Čest baseline koji dobro skalira:
- Retry 2 puta za mrežne greške i 5xx
- Bez retry-a za 4xx osim 408 i 429
- Eksponencijalni backoff s maksimalnim kašnjenjem
import { QueryClient } from "@tanstack/react-query";
export const queryClient = new QueryClient({
defaultOptions: {
queries: {
retry: (failureCount, error: any) => {
const status = error?.status ?? error?.response?.status;
if (status === 401 || status === 403 || status === 404) return false;
if (status === 429) return failureCount < 3;
if (status >= 500) return failureCount < 2;
return failureCount < 2;
},
retryDelay: (attempt) => Math.min(1000 * 2 ** attempt, 8000),
staleTime: 30_000,
gcTime: 10 * 60_000,
refetchOnWindowFocus: false,
},
mutations: {
retry: false,
},
},
});Ovo sprječava da korisnike “kažnjavate” dugim petljama tipa “loading pa error”. Također smanjuje pritisak na backend jer su retry-evi ograničeni i odgođeni.
Za strategije cacheiranja, paginacije i invalidacije koje smanjuju lance refetchanja sklone greškama, pogledajte React Query invalidacija cachea, paginacija i mutacije na razini skale.
SWR: retry s namjerom, ne po defaultu#
SWR default postavke mogu biti preagresivne za neke proizvode, posebno uz revalidaciju na fokus i nestabilne mreže. Postavite eksplicitne politike.
import useSWR from "swr";
const fetcher = (url: string) => fetch(url).then((r) => {
if (!r.ok) {
const err: any = new Error("Request failed");
err.status = r.status;
throw err;
}
return r.json();
});
export function useCustomers() {
return useSWR("/api/customers", fetcher, {
revalidateOnFocus: false,
shouldRetryOnError: (err) => {
if (err?.status === 401 || err?.status === 403 || err?.status === 404) return false;
return true;
},
errorRetryCount: 2,
errorRetryInterval: 1000,
dedupingInterval: 2000,
});
}Anti-pattern: retry mutacija bez idempotentnosti#
Ako retryate mutaciju poput “Create invoice” nakon timeouta, možete stvoriti duplikate. To nije teorija. Događa se na slabim mobilnim mrežama i tijekom backend deploya.
Ako morate retryati upise:
- Koristite idempotency keys na serveru
- Vraćajte stabilne identifikatore
- Pokažite “processing” stanje umjesto da ponovno šaljete zahtjev
⚠️ Upozorenje: Isključiti retry za mutacije sigurnije je nego ga uključiti naslijepo. Ako ne možete jamčiti idempotentnost, retry je bug integriteta podataka, a ne UX značajka.
# Pretvaranje grešaka podataka u UX: inline, toastovi i tokovi oporavka#
Obrazac koji se dobro skalira razlikuje lokalne i globalne greške.
- Inline poruke su najbolje kad se greška odnosi na određeni dio UI-ja ili polje forme.
- Toastovi su najbolji za događaje koji presijecaju aplikaciju, događaju se u pozadini ili su rezultat korisničke akcije koja je već završena.
Inline greške: najbolje za forme, panele i widgete#
Inline poruke trebaju uključiti:
- Što nije uspjelo, jednostavnim jezikom
- Sljedeći korak, poput retry ili refresh
- Opcionalni detalj ako pomaže, npr. “Provjerite vezu”
Obrazac za grešku widgeta:
- Naslov: “Nije moguće učitati kupce”
- Gumb: Retry
- Sekundarno: “Prikaži cacheirane podatke” ako je dostupno
Toastovi: najbolje za ishode spremanja i pozadinske poslove#
Toastovi funkcioniraju kada su:
- Kratki
- Usmjereni na akciju
- Ne ponavljaju se u petljama
Primjeri koji skaliraju:
- “Saved” s Undo akcijom
- “Upload failed” s Retry
- “Session expired” sa Sign in
Anti-pattern: prikazivati toast na svaku grešku refetcha u pozadini. Korisnici dožive “toast spam” i počnu ga ignorirati.
Obrazac: taksonomija poruka i kodovi grešaka#
Napravite zajedničku taksonomiju grešaka kako bi poruke bile dosljedne kroz timove.
| Klasa greške | Tipičan izvor | Poruka korisniku | UI pozicija |
|---|---|---|---|
| AuthError | 401, 403 | “Molimo prijavite se ponovno.” | Granica rute ili modal |
| NotFoundError | 404 | “Ova stavka više ne postoji.” | Inline na stranici detalja |
| RateLimitError | 429 | “Previše zahtjeva. Pokušajte ponovno za minutu.” | Inline, ponekad toast |
| ValidationError | 422 | Poruke po poljima | Inline po polju |
| NetworkError | offline, timeout | “Provjerite vezu i pokušajte ponovno.” | Inline, ponekad toast |
| UnknownError | neočekivano | “Nešto je pošlo po zlu.” + opcija prijave | Fallback granice |
Ova taksonomija olakšava odlučiti treba li retry i gdje prikazati poruku.
# Praktična implementacija: jedan error model koji se koristi svugdje#
Najbrži put do dosljednog ponašanja je normalizirati greške na razini API klijenta.
Normalizirajte Fetch greške u tipizirani oblik#
Neka oblik bude jednostavan i serializable.
export type AppError = {
name: string;
message: string;
status?: number;
code?: string;
retriable?: boolean;
};
export async function api<T>(url: string, init?: RequestInit): Promise<T> {
const res = await fetch(url, init);
if (!res.ok) {
const err: AppError = {
name: "ApiError",
message: "Request failed",
status: res.status,
retriable: res.status >= 500 || res.status === 429,
};
throw err;
}
return res.json() as Promise<T>;
}Sada vaš TanStack Query ili SWR kod može temeljiti retry i UI logiku na status i retriable.
Obrazac: bacite grešku u renderu samo za “must have” podatke#
Ako se komponenta ne može renderirati bez podataka, možete baciti grešku iz rendera kako bi je uhvatila error boundary, ali samo ako je greška već u memoriji.
Konceptualni primjer:
- Hook za podatke vrati error objekt
- Ako ekran ne može funkcionirati, bacite taj error u renderu
- Inače pokažite inline error stanje
Ovo izbjegava miješanje boundary logike u svaku child komponentu.
ℹ️ Napomena: Error boundaries i dalje ne hvataju automatski promise rejections. Hook mora već biti u error stanju tijekom renderiranja da bi “throw” obrazac radio.
# Anti-patterns koji se stalno pojavljuju u TanStack Query i SWR#
Ovi obrasci stvaraju nepouzdan UX i otežavaju debugiranje incidenata.
Anti-pattern 1: korištenje onError za toast na svaku query grešku#
Ako se lista refetcha svakih 30 sekundi, tijekom djelomičnog outagea spamate korisnike. Radije koristite inline stanja i toastajte samo na akcije koje je korisnik pokrenuo.
Bolji obrazac:
- Queryji renderiraju inline error UI unutar pogođenog dijela
- Mutacije prikazuju toastove jer je korisnik inicirao akciju
Anti-pattern 2: tretirati 404 kao “Error” svugdje#
U mnogim proizvodima 404 je valjan ishod, poput “Nema rezultata” ili “Stavka je obrisana”. Kada ima smisla, mapirajte ga na empty state.
Primjer:
- Customer not found na stranici detalja treba pokazati ekran “Kupac nije pronađen” s navigacijom
- Search results 404 treba biti “Nema rezultata”, a ne greška
Anti-pattern 3: beskonačni retry uz focus revalidation#
Default revalidacija na fokus plus retry može stvoriti petlju:
- Korisnik promijeni tab
- Aplikacija refetcha
- Server vrati 500
- Retry se okine
- Korisnik opet promijeni tab
- Još refetcheva
Riješite to eksplicitnim postavkama i ograničenjima, kao ranije.
Anti-pattern 4: brisanje cachea na svaku grešku#
Neki timovi pozivaju queryClient.clear() tijekom auth grešaka. To je disruptivno i može pokrenuti kaskade refetchanja. Radije koristite ciljanu invalidaciju i redirect flow.
Ako trebate razumjeti obrasce invalidacije koji izbjegavaju kaskadne kvarove, koristite ovaj vodič za React Query na razini skale.
# Testiranje rukovanja greškama: tretirajte kvarove kao scenarij prve klase#
Ne možete vjerovati rukovanju greškama koje ne testirate. Praktičan baseline je testirati:
- Fallback granice widgeta se prikazuje kada child baci grešku
- Granica rute se prikazuje na loader ili page failure
- Retry ponašanje queryja je ograničeno
- Inline error poruke se pojavljuju i retry radi
- Neuspjele mutacije pokažu jedan toast, ne petlju
Pouzdan toolchain je Vitest, React Testing Library i MSW za mockanje mreže. Workflow je obrađen u React strategiji testiranja s Vitestom, React Testing Library i MSW.
Primjer: test fallbacka granice komponente.
import { render, screen } from "@testing-library/react";
import React from "react";
import { ErrorBoundary } from "./ErrorBoundary";
function Boom() {
throw new Error("boom");
}
it("shows fallback when child throws", () => {
render(
<ErrorBoundary
name="Test"
fallback={() => <div>Fallback</div>}
>
<Boom />
</ErrorBoundary>
);
expect(screen.getByText("Fallback")).toBeInTheDocument();
});# Operativni aspekti: logiranje, korelacija i podrška#
Dobar UX nije dovoljan. Trebate i brzo debugiranje.
Minimalni operativni zahtjevi:
- Logirajte greške iz granica s kontekstom, poput naziva značajke i rute
- Uključite correlation ID iz backend odgovora ako postoji
- Zabilježite je li korisnik kliknuo retry i je li uspjelo
To pretvara “srušilo se” u incident koji se može riješiti.
Pragmatičan pristup je dodati metapodatke u vaš boundary onError i klijente za dohvat podataka, pa ih proslijediti u vaš observability stack. Držite metapodatke stabilnima kako bi dashboardi bili upotrebljivi.
# Ključne poruke#
- Postavite component error boundaries oko neovisnih widgeta kako jedan kvar ne bi srušio cijelu stranicu.
- Koristite route boundaries za kvarove na razini stranice i ponudite retry plus sigurnu navigaciju.
- Namjerno konfigurirajte data retry: retryajte idempotentna čitanja uz ograničenja i backoff, izbjegavajte retry mutacija osim ako imate idempotentnost.
- Preferirajte inline error stanja za lokalne probleme i toastove za ishode koje je korisnik pokrenuo te događaje koji presijecaju aplikaciju.
- Standardizirajte taksonomiju grešaka i normalizirani oblik greške kako bi retry logika i UX bili dosljedni kroz timove.
# Zaključak#
React aplikacije u 2026. pucaju na više mjesta nego samo u renderiranju, pa pouzdanost koja se može skalirati dolazi iz slojevitih obrazaca rukovanja greškama u Reactu: granice za izolaciju UI-ja, fallbackovi na razini rute za oporavak stranice, ponovni pokušaji u podatkovnom sloju za prolazne kvarove i UX poruke koje ostaju mirne i usmjerene na akciju.
Ako želite pomoć u implementaciji ovih obrazaca kroz Next.js ili React codebase, Samioda može napraviti audit vaših trenutnih error flowova, ujednačiti politike retry-a za TanStack Query ili SWR i isporučiti dosljedan sustav fallbackova i poruka koji poboljšava i UX i incident response. Javite se putem Samioda kako biste dobili praktičan plan i produkcijski spremnu implementaciju.
FAQ
Osnivač i senior developer u Samiodi. 8+ godina iskustva u izradi React, Next.js, Flutter i n8n rješenja za klijente diljem Europe.
Više iz kategorije Web razvoj
Sve →Next.js + Supabase Edge Functions: Praktična arhitektura za moderni SaaS (vodič za 2026.)
Produkcijski spremna Next.js + Supabase Edge Functions arhitektura za SaaS: što ide u Edge Functions, a što u API rute/server actions, kako strukturirati module te kako sigurno deployati na Vercel ili Cloudflare.
Testiranje ugovora za React komponente: MSW + Storybook kao živi API mockovi
Praktičan vodič za testiranje ugovora React komponenti pomoću zajedničkih MSW handlera u Storybooku i automatiziranim testovima kako biste spriječili razilaženje mockova, nestabilan UI i regresije u CI-u.
React obrasci za tablice podataka u velikom opsegu: virtualizacija, pinanje stupaca, filteri i izvoz (TanStack Table + Virtual)
Produkcijski spremni obrasci za React tablice podataka uz TanStack Table i TanStack Virtual: arhitektura stanja, paginacija i sortiranje na strani servera, debounce filteri, pinanje stupaca, izvozi i zamke performansi.
Trebate pomoć s projektom?
Gradimo prilagođena rješenja koristeći tehnologije iz ovog članka. Senior tim, fiksne cijene.
Povezani članci
Next.js App Router obrasci u 2026.: React Hook Form + Zod + Server Actions (validacija, greške, UX)
Arhitektura spremna za produkciju za Next.js App Router obrasce uz React Hook Form, Zod i Server Actions — sa zajedničkim shemama, sigurnom validacijom na serveru, async provjerama, uploadom datoteka i dosljednim rukovanjem greškama.
UX obrasci u Next.js App Routeru: Error Boundaries, Loading UI i streaming kako treba
Praktičan vodič za otporan UX u Next.js App Routeru: struktura route segmenata, error.tsx, loading.tsx, not-found.tsx i Suspense streaming obrasci za djelomično renderiranje, sigurnije dohvaćanje podataka i manje pomaka layouta.
React Query u velikim aplikacijama: invalidacija cachea, paginacija i obrasci mutacija za stvarne aplikacije
Najbolje prakse invalidacije cachea u React Queryju za aplikacije iz stvarnog svijeta: skalabilan dizajn query keyjeva, strategija invalidacije, optimistična ažuriranja, infinite queryji i pozadinski refetch u Next.js App Routeru.