Rules Hub
Coding Rules Library
Rule priority, scope & exceptions
Use this to align rules with the senior-level structure (P0/P1/P2, scope, exceptions/tradeoffs).
Avoid request waterfalls in UI data fetching
Start independent requests in parallel to reduce total loading time.
Bad example
| 1 | import { useEffect, useState } from "react"; |
| 2 |
|
| 3 | export function Page() { |
| 4 | const [user, setUser] = useState<any>(null); |
| 5 |
|
| 6 | useEffect(function fetchUser() { |
| 7 | fetch("/api/user") |
| 8 | .then((r) => r.json()) |
| 9 | .then(setUser); |
| 10 | }, []); |
| 11 |
|
| 12 | if (!user) return <div>Loading user...</div>; |
| 13 |
|
| 14 | // Child fetch only starts after user is loaded => waterfall. |
| 15 | return <Orders userId={user.id} />; |
| 16 | } |
| 17 |
|
| 18 | function Orders({ userId }: { userId: string }) { |
| 19 | const [orders, setOrders] = useState<any[] | null>(null); |
| 20 |
|
| 21 | useEffect(function fetchOrders() { |
| 22 | fetch(`/api/orders?userId=${userId}`) |
| 23 | .then((r) => r.json()) |
| 24 | .then(setOrders); |
| 25 | }, [userId]); |
| 26 |
|
| 27 | if (!orders) return <div>Loading orders...</div>; |
| 28 | return <pre>{JSON.stringify(orders, null, 2)}</pre>; |
| 29 | } |
Explanation (EN)
Orders are fetched only after the user request completes. For pages with multiple independent data sources, this sequential loading increases total time to interactive.
Objašnjenje (HR)
Narudzbe se dohvaćaju tek nakon sto se user request zavrsi. Na stranicama s vise neovisnih izvora podataka, ovakav sekvencijalni load povecava ukupno vrijeme do prikaza.
Good example
| 1 | import { useEffect, useState } from "react"; |
| 2 |
|
| 3 | type User = { id: string; name: string }; |
| 4 | type Order = { id: string; total: number }; |
| 5 |
|
| 6 | export function Page() { |
| 7 | const [user, setUser] = useState<User | null>(null); |
| 8 | const [orders, setOrders] = useState<Order[] | null>(null); |
| 9 |
|
| 10 | useEffect(function fetchAll() { |
| 11 | let isActive = true; |
| 12 |
|
| 13 | Promise.all([ |
| 14 | fetch("/api/user").then((r) => r.json() as Promise<User>), |
| 15 | fetch("/api/orders").then((r) => r.json() as Promise<Order[]>) |
| 16 | ]).then(([u, o]) => { |
| 17 | if (!isActive) return; |
| 18 | setUser(u); |
| 19 | setOrders(o); |
| 20 | }); |
| 21 |
|
| 22 | return () => { |
| 23 | isActive = false; |
| 24 | }; |
| 25 | }, []); |
| 26 |
|
| 27 | if (!user || !orders) return <div>Loading...</div>; |
| 28 |
|
| 29 | return ( |
| 30 | <div> |
| 31 | <h1>{user.name}</h1> |
| 32 | <pre>{JSON.stringify(orders, null, 2)}</pre> |
| 33 | </div> |
| 34 | ); |
| 35 | } |
Explanation (EN)
Independent requests run in parallel and the UI updates once both are ready. This reduces total load time and avoids the waterfall pattern.
Objašnjenje (HR)
Neovisni requestovi se pokrecu paralelno i UI se osvjezi kada su oba spremna. To smanjuje ukupno vrijeme i izbjegava waterfall.
Notes (EN)
In SSR frameworks (Next.js), prefer parallel fetching on the server (route handlers/loaders) where possible to avoid client waterfalls.
Bilješke (HR)
U SSR frameworkovima (Next.js), preferiraj paralelno dohvaćanje na serveru (route handleri/loaderi) gdje je moguce kako bi izbjegao client waterfall.
Exceptions / Tradeoffs (EN)
If one request truly depends on the result of another (needs an ID), sequential fetching is required.
Iznimke / Tradeoffi (HR)
Ako jedan request stvarno ovisi o rezultatu drugog (treba ID), sekvencijalno dohvaćanje je potrebno.