Architektura zásobníku modálních oken v React pro škálovatelné aplikace
Ve složitých React aplikacích se modální okna rychle stávají zdrojem problémů. Jedno modální okno s jednoduchým useState funguje izolovaně, ale s růstem projektu se objevuje duplikace komponent, prop drilling a závislost stavů. Řešením je typizovaný zásobník overlays s rozdělením logiky podle zdrojů volání.
Uvažujme typický případ: administrátorský panel pro tržiště s tabulkou produktů. Každý řádek vyžaduje modální okno pro zadání značení (IMEI, SGTIN), včetně tabulky objednávek, validace, nahrávání Excelu a vícekrokového procesu.
Problémy místního vykreslování
Vykreslování modálního okna IdentificationModal v každém řádku tabulky vede k tisícům skrytých instancí v DOM. Komponent obsahuje tabulky, vstupy s maskami, RTK Query háky, stepper – zatížení paměti je kritické.
Příklad neúspěšného přístupu:
const ProductRow = ({ product }) => {
const [isOpen, setIsOpen] = useState(false);
return (
<tr>
<td>{product.name}</td>
<td>
<Button onClick={() => setIsOpen(true)}>Zadat značení</Button>
<IdentificationModal
isOpen={isOpen}
onClose={() => setIsOpen(false)}
productId={product.id}
/>
</td>
</tr>
);
};
Závěr: modální okno by mělo být vykresleno pouze jednou na úrovni stránky.
Prop drilling a globální stav
Přesunutí modálního okna nahoru řeší problém s DOM, ale vyžaduje předávání zpětných volání přes 4–5 úrovní komponent:
ProductsPage→ProductsTable→TableBody→ProductRow→ActionsMenu
Redux s dispečováním openIdent(productId) eliminuje propsy, ale komplikuje se s více kontexty.
Mnohočetné zdroje dat
Modální okno se používá ze tří míst s různými API:
| Zdroj | Objednávky | Nahrávání dat | Excel šablona |
|----------|--------|-----------------|--------------|
| Hlavička tabulky | Všechny objednávky | ordersApi.getIdentOrders() | ordersApi.getExcel() |
| Řádek tabulky | Jedna objednávka | Filtr podle orderId | ordersApi.getExcel() |
| Košík | Objednávky košíku | cartsApi.getCartIdentOrders(cartId) | cartsApi.getExcel() |
Zahrnutí logiky do modálního okna vytváří God Object s if-y pro každé API, invalidací cache, analytikou. Alternativa – čisté UI s propsy orders, onSave, ale stránka nezná kontext předem.
Závislosti mezi modálními okny
Otevření identifikace skrývá panel hromadných akcí, uzavření ji vrací. Přidání modálního okna potvrzení («Neuložená data») vyžaduje ruční správu příznaků:
openConfirmClose: (state) => {
state.isConfirmOpen = true;
state.isIdentOpen = false;
},
cancelConfirmClose: (state) => {
state.isConfirmOpen = false;
state.isIdentOpen = true;
},
Plochá struktura příznaků neodráží pořadí překrytí a strategie návratu.
Architektura řešení: typizovaný zásobník
Systém řeší tři problémy současně:
- Jednotné vykreslování přes Redux-zásobník s frontou overlays.
- Rozdělení kontextů přes source-obaly.
- Automatická správa závislostí pravidly zásobníku.
Redux-slice pro zásobník
export enum EOverlayName {
BulkActions = 'bulkActions',
IdentFromHeader = 'identFromHeader',
IdentFromRow = 'identFromRow',
IdentFromCart = 'identFromCart',
EditProduct = 'editProduct',
// ...
}
type OverlayItem<TData = unknown> = {
name: EOverlayName;
data: TData;
priority?: number;
};
type OverlayStack = OverlayItem[];
Zásobník uchovává typizovaná data a pořadí. Asynchronní akce otevírají overlay s payload.
Overlay-háky pro spouštěče
const useOverlay = () => {
const dispatch = useDispatch();
const open = <TData>(name: EOverlayName, data: TData) => {
dispatch(openOverlay({ name, data }));
};
const close = (name: EOverlayName) => {
dispatch(closeOverlay(name));
};
return { open, close };
};
Tlačítko v řádku: open('identFromRow', { productId, orderId }). Modální okno čte zásobník bez prop drilling.
Source-obaly pro kontexty
Každý zdroj exportuje adaptér:
// identFromRow.adapter.ts
export const useIdentFromRowAdapter = () => {
const { open } = useOverlay();
const openIdent = (productId: string, orderId: string) => {
const orders = ordersApi.useGetIdentOrdersQuery();
const filtered = orders.data?.filter(o => o.id === orderId);
open('identFromRow', { orders: filtered, productId });
};
return { openIdent };
};
Modální okno dostává hotová orders, onSave bez znalosti zdrojů.
Strategie otevírání a uzavírání
Zásobník podporuje pravidla:
- Nahrazení: otevření
identzavírábulkActions. - Pauza: uzavření vrací předchozí.
- Vymazání: potvrzení resetuje zásobník.
const openOverlay = createAsyncThunk(
'overlay/open',
async ({ name, data }, { getState }) => {
const stack = selectStack(getState());
// Logika nahrazení/priorit
return { name, data };
}
);
Co je důležité
- Zásobník řeší prop drilling, duplikaci DOM a závislosti stavů jednou architekturou.
- Source-obaly izolují API-logiku, modální okno zůstává čisté UI.
- Typizace
OverlayItem<TData>zajišťuje bezpečnost dat. - Asynchronní strategie automatizují pořadí otevírání/uzavírání.
- Škáluje se na desítky modálních oken bez zvýšení složitosti reduktoru.
— Editorial Team
Zatím žádné komentáře.