Rules Hub
Code Ruleset
Rule priority, scope & exceptions
Use this to align rules with the senior-level structure (P0/P1/P2, scope, exceptions/tradeoffs).
Cancel in-flight requests with AbortController
Abort old requests on unmount or param changes to prevent race conditions and wasted work.
Bad example
| 1 | import { useEffect, useState } from "react"; |
| 2 |
|
| 3 | export function SearchResults({ query }: { query: string }) { |
| 4 | const [results, setResults] = useState<string[]>([]); |
| 5 |
|
| 6 | useEffect(function fetchResults() { |
| 7 | fetch(`/api/search?q=${encodeURIComponent(query)}`) |
| 8 | .then((r) => r.json()) |
| 9 | .then((data) => setResults(data.items)); |
| 10 | }, [query]); |
| 11 |
|
| 12 | return <pre>{JSON.stringify(results, null, 2)}</pre>; |
| 13 | } |
Explanation (EN)
An obsolete response can overwrite the latest state; cancellation and a final commit guard address different parts of that race.
Objašnjenje (HR)
Zastarjeli odgovor može prepisati novije stanje. Otkazivanje i završna provjera prije zapisa rješavaju različite dijelove te utrke.
Good example
| 1 | useEffect(() => { |
| 2 | const controller = new AbortController(); |
| 3 | void (async () => { |
| 4 | try { |
| 5 | const response = await fetch(`/api/search?q=${encodeURIComponent(query)}`, { signal: controller.signal }); |
| 6 | if (!response.ok) throw new Error('Search failed'); |
| 7 | const data = await response.json(); |
| 8 | if (!controller.signal.aborted) setResults(data.items); |
| 9 | } catch (error) { |
| 10 | if (error === controller.signal.reason) return; |
| 11 | reportSearchError(error); |
| 12 | } |
| 13 | })(); |
| 14 | return () => controller.abort(); |
| 15 | }, [query]); |
Explanation (EN)
The lifecycle cancels the request, guards the final state commit and handles other failures instead of creating an unobserved rejection.
Objašnjenje (HR)
Životni ciklus prekida zahtjev, štiti završni zapis stanja i obrađuje ostale pogreške bez neopaženog odbijanja promisea.
Notes (EN)
Cancel obsolete abortable work when its lifecycle ends, then guard commits that can occur after cancellation or through non-abortable transformation steps. Handle failures inside the owned task.
Bilješke (HR)
Prekini zastarjeli posao koji podržava otkazivanje kada završi njegov životni ciklus. Dodatno zaštiti zapis rezultata nakon otkazivanja i korake koji se ne mogu prekinuti. Obradi pogreške unutar vlasnika posla.
Exceptions / Tradeoffs (EN)
Do not abort fire-and-forget requests that must complete (e.g., audit logs). Decouple them from UI state instead. Aborting a client request does not guarantee the server mutation was rolled back. Shared work may belong to a longer lifecycle and must not be cancelled by one subscriber.
Iznimke / Tradeoffi (HR)
Posao koji mora završiti, primjerice obavezni audit zapis, odvoji od životnog ciklusa UI-ja. Prekid klijentskog zahtjeva ne jamči rollback serverske mutacije. Zajednički posao može imati dulji životni ciklus pa ga jedan pretplatnik ne smije otkazati svima.