Zurück zur Startseite

React Modal Stack: Architektur für IT

Der Artikel beschreibt die Architektur eines typisierten Modal-Stacks für React-Anwendungen. Löst DOM-Duplikationsprobleme, Prop Drilling und Abhängigkeiten über Redux Slice, Overlay Hooks und Source Wrappers. Bietet Code-Beispiele und Skalierungsstrategien.

Typisierter Modals Stack in React für komplexe UI
Advertisement 728x90

React Modal-Stack-Architektur für skalierbare Apps

In komplexen React-Apps werden Modals schnell zum Albtraum. Ein einzelnes Modal mit einfachem useState funktioniert isoliert gut, aber mit wachsendem Projekt entstehen duplizierte Komponenten, Prop Drilling und verflochtene Zustandsabhängigkeiten. Die Lösung? Ein typsicherer Overlay-Stack, der die Logik nach Auslöserquellen trennt.

Stellen Sie sich ein typisches Admin-Dashboard eines Marktplatzes mit Produkttabelle vor. Jede Zeile braucht ein Modal zum Hinzufügen von Kennungen (IMEI, SGTIN), inklusive Bestelltabelle, Validierung, Excel-Uploads und einem mehrstufigen Assistenten.

Probleme bei lokaler Darstellung

Das Rendern eines IdentificationModal in jeder Tabellenzeile erzeugt Tausende versteckte DOM-Elemente. Die Komponente ist vollgepackt mit Tabellen, maskierten Eingaben, RTK-Query-Hooks und einem Stepper – der Speicherverbrauch explodiert.

Google AdInline article slot

Hier ein schlechtes Beispiel:

const ProductRow = ({ product }) => {
  const [isOpen, setIsOpen] = useState(false);

  return (
    <tr>
      <td>{product.name}</td>
      <td>
        <Button onClick={() => setIsOpen(true)}>Kennungen hinzufügen</Button>
        <IdentificationModal
          isOpen={isOpen}
          onClose={() => setIsOpen(false)}
          productId={product.id}
        />
      </td>
    </tr>
  );
};

Wichtig: Modals sollten einmalig auf Seitenebene gerendert werden.

Prop Drilling und globaler Zustand

Das Verschieben des Modals höher in den Baum behebt den DOM-Bloat, erfordert aber das Durchreichen von Callbacks durch 4–5 Komponentenebenen:

Google AdInline article slot
  • ProductsPageProductsTableTableBodyProductRowActionsMenu

Redux mit openIdent(productId)-Dispatches eliminiert Props, wird aber bei mehreren Kontexten unübersichtlich.

Mehrere Datenquellen

Das Modal zieht Daten aus drei Quellen mit unterschiedlichen APIs:

| Auslöserquelle | Bestellungen | Datenladen | Excel-Vorlage |

Google AdInline article slot

|---------------|--------------|------------|---------------|

| Tabellenkopf | Alle Bestellungen | ordersApi.getIdentOrders() | ordersApi.getExcel() |

| Tabellenzeile | Einzelne Bestellung | Filter nach orderId | ordersApi.getExcel() |

| Warenkorb | Warenkorb-Bestellungen | cartsApi.getCartIdentOrders(cartId) | cartsApi.getExcel() |

Das Stopfen der Logik ins Modal macht es zu einem God Object voller if-Statements, Cache-Invalidierung und Analysen. Die Alternative – ein reines UI-Modal mit orders und onSave-Props – scheitert, da die Seite den Kontext nicht im Voraus kennt.

Modal-Abhängigkeiten

Das Öffnen des Kennungsmodals versteckt das Bulk-Actions-Panel; Schließen bringt es zurück. Ein Bestätigungsmodal („Ungespeicherte Änderungen?“) erfordert manuelles Jonglieren mit Flags:

openConfirmClose: (state) => {
  state.isConfirmOpen = true;
  state.isIdentOpen = false;
},
cancelConfirmClose: (state) => {
  state.isConfirmOpen = false;
  state.isIdentOpen = true;
},

Flache Flag-Strukturen erfassen weder Stapelreihenfolge noch Rollback-Strategien.

Lösungsarchitektur: Typsicherer Stack

Dieses System löst drei Probleme auf einmal:

  • Einmaliges Rendering über einen Redux-Stack mit Overlay-Warteschlange.
  • Kontexttrennung durch Quellen-Wrapper.
  • Automatische Abhängigkeitsverwaltung mit Stack-Regeln.

Redux-Slice für den Stack

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[];

Der Stack speichert typsichere Daten und Reihenfolge. Asynchrone Aktionen pushen Overlays mit Payloads.

Overlay-Hooks für Auslöser

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 };
};

Zeilen-Button: open('identFromRow', { productId, orderId }). Das Modal liest aus dem Stack – kein Prop Drilling.

Quellen-Wrapper für Kontexte

Jede Quelle exportiert einen Adapter:

// 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 };
};

Das Modal erhält vorbereitete orders und onSave, ohne die Quelle zu kennen.

Open/Close-Strategien

Der Stack erzwingt Regeln:

  • Ersetzung: Öffnen von ident schließt bulkActions.
  • Pause: Schließen setzt vorherigen Overlay fort.
  • Löschen: Bestätigung setzt Stack zurück.
const openOverlay = createAsyncThunk(
  'overlay/open',
  async ({ name, data }, { getState }) => {
    const stack = selectStack(getState());
    // Ersetzungs-/Prioritätslogik
    return { name, data };
  }
);

Wichtige Vorteile

  • Der Stack eliminiert Prop Drilling, DOM-Duplikate und Zustandsabhängigkeiten mit einer Architektur.
  • Quellen-Wrapper isolieren API-Logik; Modals bleiben reines UI.
  • OverlayItem<TData>-Typisierung sichert Daten.
  • Asynchrone Strategien automatisieren Open/Close-Reihenfolge.
  • Skaliert auf Dutzende Modals ohne Reducer-Bloat.

— Editorial Team

Advertisement 728x90

Weiterlesen