Heim >Web-Frontend >js-Tutorial >Ereignisgesteuerte Architektur für die Kommunikation von Clean React-Komponenten

Ereignisgesteuerte Architektur für die Kommunikation von Clean React-Komponenten

Barbara Streisand
Barbara StreisandOriginal
2024-12-06 15:26:13341Durchsuche

Haben Sie genug von dem endlosen Wirrwarr von Requisitenbohrungen und Rückrufketten in Ihren React-Anwendungen? Fühlt sich die Verwaltung des Zustands und der Kommunikation zwischen tief verschachtelten Komponenten wie ein Ringen mit Spaghetti-Code an?

Eine ereignisgesteuerte Architektur kann Ihre Komponenteninteraktionen vereinfachen, die Komplexität reduzieren und Ihre App wartbarer machen. In diesem Artikel zeige ich Ihnen, wie Sie einen benutzerdefinierten useEvent-Hook verwenden, um Komponenten zu entkoppeln und die Kommunikation in Ihrer React-App zu verbessern.

Lassen Sie mich Sie durch die Sache führen. Beginnen wir mit


Das Problem: Props-Drilling und Callback-Ketten

In der modernen Anwendungsentwicklung kann die Verwaltung des Status und der Kommunikation zwischen Komponenten schnell mühsam werden. Dies gilt insbesondere in Szenarien mit Requisiten-Drilling – bei denen Daten über mehrere Ebenen verschachtelter Komponenten weitergegeben werden müssen – und Rückrufketten, was zu einer verworrenen Logik führen und die Codeausführung erschweren kann pflegen oder debuggen.

Diese Herausforderungen führen häufig zu eng gekoppelten Komponenten, verringern die Flexibilität und erhöhen die kognitive Belastung für Entwickler, die versuchen, den Datenfluss durch die Anwendung zu verfolgen. Ohne einen besseren Ansatz kann diese Komplexität die Entwicklung erheblich verlangsamen und zu einer brüchigen Codebasis führen.

Der traditionelle Ablauf: Requisiten runter, Rückrufe hoch

In einer typischen React-Anwendung übergeben übergeordnete Komponenten Requisiten an ihre untergeordneten Komponenten, und untergeordnete Komponenten kommunizieren mit dem übergeordneten Element, indem sie Rückrufe auslösen. Dies funktioniert gut für flache Komponentenbäume, aber je tiefer die Hierarchie wird, desto chaotischer wird es:

Props Drilling: Daten müssen manuell über mehrere Komponentenebenen weitergegeben werden, auch wenn dies nur für die tiefste Komponente erforderlich ist.

Rückrufketten: Ebenso müssen untergeordnete Komponenten Ereignishandler im Baum nach oben weiterleiten, wodurch eng gekoppelte und schwer zu wartende Strukturen entstehen.

Ein häufiges Problem: Callback-Komplexität

Nehmen Sie zum Beispiel dieses Szenario:

  • Der Elternteil übergibt Requisiten an Kinder A.
  • Von dort aus werden die Requisiten auf GrandChildren A/B und schließlich auf SubChildren N verzweigt.
  • Wenn SubChildren N das übergeordnete Element über ein Ereignis benachrichtigen muss, löst es einen Rückruf aus, der durch jede Zwischenkomponente zurückgeht.

Diese Einrichtung wird schwieriger zu verwalten, wenn die Anwendung wächst. Zwischenkomponenten fungieren oft nur als Mittelsmänner, die Requisiten und Rückrufe weiterleiten, was den Code aufbläht und die Wartbarkeit verringert.

Event-Driven Architecture for Clean React Component Communication

Um Props Drilling anzugehen, greifen wir häufig auf Lösungen wie globale Zustandsverwaltungsbibliotheken (z. B. Zustand) zurück, um den Datenaustausch zu optimieren. Aber wie sieht es mit der Verwaltung von Rückrufen aus?

Hier kann ein ereignisgesteuerter Ansatz bahnbrechend sein. Durch die Entkopplung von Komponenten und die Verwendung von Ereignissen zur Abwicklung von Interaktionen können wir die Rückrufverwaltung erheblich vereinfachen. Lassen Sie uns untersuchen, wie dieser Ansatz funktioniert.


Die Lösung: Entscheiden Sie sich für den ereignisgesteuerten Ansatz

Event-Driven Architecture for Clean React Component Communication

Anstatt sich bei der Kommunikation im Baum auf direkte Rückrufe zu verlassen, entkoppelt eine ereignisgesteuerte Architektur Komponenten und zentralisiert die Kommunikation. So funktioniert es:

Ereignisverteilung

Wenn SubChildren N ein Ereignis auslöst (z. B. onMyEvent), ruft es nicht direkt einen Rückruf im übergeordneten Element auf.
Stattdessen wird ein Ereignis ausgelöst, das von einem zentralen Ereignishandler verarbeitet wird.

Zentralisierte Abwicklung

Der Ereignishandler wartet auf das ausgelöste Ereignis und verarbeitet es.
Es kann das übergeordnete Element (oder eine andere interessierte Komponente) benachrichtigen oder bei Bedarf zusätzliche Aktionen auslösen.

Requisiten bleiben nach unten

Requisiten werden weiterhin in der Hierarchie weitergegeben, um sicherzustellen, dass Komponenten die Daten erhalten, die sie zum Funktionieren benötigen.

Dies kann mit zentralisierten Zustandsverwaltungstools wie Zustand und Redux gelöst werden, wird aber in diesem Artikel nicht behandelt.


Durchführung

Aber wie implementieren wir diese Architektur?

useEvent-Hook

Lassen Sie uns einen benutzerdefinierten Hook namens useEvent erstellen. Dieser Hook ist für die Abwicklung des Ereignisabonnements und die Rückgabe einer Versandfunktion zum Auslösen des Zielereignisses verantwortlich.

Da ich Typoskript verwende, muss ich die Ereignisschnittstelle des Fensters erweitern, um benutzerdefinierte Ereignisse zu erstellen:

interface AppEvent<PayloadType = unknown> extends Event {
  detail: PayloadType;
}

export const useEvent = <PayloadType = unknown>(
  eventName: keyof CustomWindowEventMap,
  callback?: Dispatch<PayloadType> | VoidFunction
) => {
  ...
};

Auf diese Weise können wir eine benutzerdefinierte Ereigniszuordnung definieren und benutzerdefinierte Parameter übergeben:

interface AppEvent<PayloadType = unknown> extends Event {
  detail: PayloadType;
}

export interface CustomWindowEventMap extends WindowEventMap {
  /* Custom Event */
  onMyEvent: AppEvent<string>; // an event with a string payload
}

export const useEvent = <PayloadType = unknown>(
  eventName: keyof CustomWindowEventMap,
  callback?: Dispatch<PayloadType> | VoidFunction
) => {
  ...
};

Da wir nun die benötigten Schnittstellen definiert haben, sehen wir uns den endgültigen Hook-Code an

import { useCallback, useEffect, type Dispatch } from "react";

interface AppEvent<PayloadType = unknown> extends Event {
  detail: PayloadType;
}

export interface CustomWindowEventMap extends WindowEventMap {
  /* Custom Event */
  onMyEvent: AppEvent<string>;
}

export const useEvent = <PayloadType = unknown>(
  eventName: keyof CustomWindowEventMap,
  callback?: Dispatch<PayloadType> | VoidFunction
) => {
  useEffect(() => {
    if (!callback) {
      return;
    }

    const listener = ((event: AppEvent<PayloadType>) => {
      callback(event.detail); // Use `event.detail` for custom payloads
    }) as EventListener;

    window.addEventListener(eventName, listener);
    return () => {
      window.removeEventListener(eventName, listener);
    };
  }, [callback, eventName]);

  const dispatch = useCallback(
    (detail: PayloadType) => {
      const event = new CustomEvent(eventName, { detail });
      window.dispatchEvent(event);
    },
    [eventName]
  );

  // Return a function to dispatch the event
  return { dispatch };
};

Der useEvent-Hook ist ein benutzerdefinierter React-Hook zum Abonnieren und Versenden benutzerdefinierter Fensterereignisse. Es ermöglicht Ihnen, auf benutzerdefinierte Ereignisse zu warten und diese mit einer bestimmten Nutzlast auszulösen.

Was wir hier tun, ist ziemlich einfach: Wir verwenden das Standard-Event-Management-System und erweitern es, um unsere individuellen Events zu ermöglichen.

Parameter:

  • eventName (Zeichenfolge): Der Name des Ereignisses, auf das gewartet werden soll.
  • Rückruf (optional): Eine Funktion, die aufgerufen wird, wenn das Ereignis ausgelöst wird, und die Nutzlast als Argument empfängt.

Merkmale:

  • Ereignis-Listener: Er wartet auf das angegebene Ereignis und ruft den bereitgestellten Rückruf mit den Ereignisdetails auf (benutzerdefinierte Nutzlast).
  • Versand von Ereignissen: Der Hook stellt eine Versandfunktion bereit, um das Ereignis mit einer benutzerdefinierten Nutzlast auszulösen.

Beispiel:

interface AppEvent<PayloadType = unknown> extends Event {
  detail: PayloadType;
}

export const useEvent = <PayloadType = unknown>(
  eventName: keyof CustomWindowEventMap,
  callback?: Dispatch<PayloadType> | VoidFunction
) => {
  ...
};

Okay, cool, aber wie wäre es mit einem

Beispiel aus der Praxis?

Schauen Sie sich diesen StackBlitz an (Wenn er nicht geladen wird, überprüfen Sie ihn bitte hier)

Dieses einfache Beispiel zeigt den Zweck des useEvent-Hooks. Im Grunde löst die Schaltfläche des Körpers ein Ereignis aus, das von Seitenleisten-, Kopf- und Fußzeilenkomponenten abgefangen und entsprechend aktualisiert wird.

Dadurch können wir Ursache-Wirkungs-Reaktionen definieren, ohne dass ein Callback an viele Komponenten weitergegeben werden muss.


Praxisnahe Anwendungsfälle für useEvent

Hier sind einige reale Anwendungsfälle, bei denen der useEvent-Hook die Kommunikation vereinfachen und Komponenten in einer React-Anwendung entkoppeln kann:


1. Benachrichtigungssystem

Ein Benachrichtigungssystem erfordert oft globale Kommunikation.

  • Szenario:

    • Wenn ein API-Aufruf erfolgreich ist, muss in der gesamten App eine „Erfolgs“-Benachrichtigung angezeigt werden.
    • Komponenten wie ein „Benachrichtigungsabzeichen“ in der Kopfzeile müssen ebenfalls aktualisiert werden.
  • Lösung: Verwenden Sie den useEvent-Hook, um ein onNotification-Ereignis mit den Benachrichtigungsdetails auszulösen. Komponenten wie das NotificationBanner und der Header können dieses Ereignis abhören und unabhängig voneinander aktualisieren.

2. Themenwechsel

Wenn ein Benutzer das Thema umschaltet (z. B. Hell-/Dunkelmodus), müssen möglicherweise mehrere Komponenten reagieren.

  • Szenario:

    • Eine ThemeToggle-Komponente löst ein benutzerdefiniertes onThemeChange-Ereignis aus.
    • Komponenten wie die Seitenleiste und der Header warten auf dieses Ereignis und aktualisieren ihre Stile entsprechend.
  • Vorteile: Der Themenstatus oder die Rückruffunktionen müssen nicht über Requisiten im gesamten Komponentenbaum übergeben werden.

3. Globale Tastenzuordnungen

Implementieren Sie globale Verknüpfungen, z. B. das Drücken von „Strg S“, um einen Entwurf zu speichern, oder „Escape“, um ein Modal zu schließen.

  • Szenario:
    • Ein globaler Keydown-Listener sendet ein onShortcutPressed-Ereignis mit den Details der gedrückten Taste.
    • Modale Komponenten oder andere UI-Elemente reagieren auf bestimmte Verknüpfungen, ohne auf übergeordnete Komponenten angewiesen zu sein, um das Schlüsselereignis weiterzuleiten.

4. Echtzeit-Updates

Anwendungen wie Chat-Apps oder Live-Dashboards erfordern mehrere Komponenten, um auf Echtzeitaktualisierungen zu reagieren.

  • Szenario:
    • Eine WebSocket-Verbindung löst onNewMessage- oder onDataUpdate-Ereignisse aus, wenn neue Daten eintreffen.
    • Komponenten wie ein Chat-Fenster, Benachrichtigungen und Zähler für ungelesene Nachrichten können Aktualisierungen unabhängig voneinander verarbeiten.

5. Formularvalidierung über Komponenten hinweg

Bei komplexen Formularen mit mehreren Abschnitten können Validierungsereignisse zentralisiert werden.

  • Szenario:
    • Eine Formularkomponente sendet onFormValidate-Ereignisse, wenn Benutzer Felder ausfüllen.
    • Eine Zusammenfassungskomponente wartet auf diese Ereignisse, um Validierungsfehler anzuzeigen, ohne eng mit der Formularlogik verknüpft zu sein.

6. Analytics-Tracking

Verfolgen Sie Benutzerinteraktionen (z. B. Schaltflächenklicks, Navigationsereignisse) und senden Sie sie an einen Analysedienst.

  • Szenario:
    • Versenden Sie onUserInteraction-Ereignisse mit relevanten Details (z. B. der Beschriftung der angeklickten Schaltfläche).
    • Ein zentraler Analysehandler wartet auf diese Ereignisse und sendet sie an eine Analyse-API.

7. Tools für die Zusammenarbeit

Für kollaborative Tools wie gemeinsame Whiteboards oder Dokumenteditoren können Ereignisse Mehrbenutzerinteraktionen verwalten.

  • Szenario:
    • Versenden Sie onUserAction-Ereignisse, wenn ein Benutzer ein Objekt zeichnet, eingibt oder verschiebt.
    • Andere Clients und UI-Komponenten warten auf diese Ereignisse, um die Änderungen in Echtzeit widerzuspiegeln.

Durch die Nutzung des useEvent-Hooks in diesen Szenarien können Sie modulare, wartbare und skalierbare Anwendungen erstellen, ohne auf tief verschachtelte Requisiten oder Rückrufketten angewiesen zu sein.


Schlussfolgerungen

Ereignisse können die Art und Weise, wie Sie React-Anwendungen erstellen, verändern, indem sie die Komplexität reduzieren und die Modularität verbessern. Fangen Sie klein an – identifizieren Sie einige Komponenten in Ihrer App, die von einer entkoppelten Kommunikation profitieren würden, und implementieren Sie den useEvent-Hook.

Mit diesem Ansatz vereinfachen Sie nicht nur Ihren Code, sondern machen ihn in Zukunft auch einfacher zu warten und zu skalieren.

Warum Ereignisse verwenden?
Ereignisse kommen zum Tragen, wenn Ihre Komponenten auf etwas reagieren müssen, das an anderer Stelle in Ihrer Anwendung passiert ist, ohne unnötige Abhängigkeiten oder komplizierte Rückrufketten einzuführen. Dieser Ansatz reduziert die kognitive Belastung und vermeidet die Fallstricke einer engen Kopplung von Komponenten.

Meine Empfehlung
Verwenden Sie Ereignisse für die Kommunikation zwischen Komponenten – wenn eine Komponente andere über eine Aktion oder Zustandsänderung benachrichtigen muss, unabhängig von ihrer Position im Komponentenbaum.
Vermeiden Sie die Verwendung von Ereignissen für die komponenteninterne Kommunikation, insbesondere für Komponenten, die eng miteinander verbunden oder direkt verbunden sind. Verlassen Sie sich für diese Szenarien auf die integrierten Mechanismen von React wie Requisiten, Status oder Kontext.

Ein ausgewogener Ansatz
Obwohl Ereignisse mächtig sind, kann ihre übermäßige Nutzung zu Chaos führen. Setzen Sie sie mit Bedacht ein, um die Kommunikation über lose verbundene Komponenten hinweg zu vereinfachen, aber lassen Sie nicht zu, dass sie die Standardtools von React zur Verwaltung lokaler Interaktionen ersetzen.

Das obige ist der detaillierte Inhalt vonEreignisgesteuerte Architektur für die Kommunikation von Clean React-Komponenten. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn