首頁 >web前端 >js教程 >用於乾淨 React 元件通訊的事件驅動架構

用於乾淨 React 元件通訊的事件驅動架構

Barbara Streisand
Barbara Streisand原創
2024-12-06 15:26:13340瀏覽

您是否厭倦了 React 應用程式中無休止的 props 鑽取和回調鏈?管理深層嵌套組件之間的狀態和通訊是否感覺就像與義大利麵條程式碼搏鬥?

事件驅動架構可以簡化元件互動、降低複雜性並使您的應用程式更易於維護。在本文中,我將向您展示如何使用自訂 useEvent 掛鉤來解耦元件並改善 React 應用程式之間的通訊。

讓我帶您了解一下,讓我們從

開始

問題:道具鑽探與回檔鏈

在現代應用程式開發中,管理元件之間的狀態和通訊很快就會變得很麻煩。在涉及props 鑽探(其中資料必須透過多層巢狀元件向下傳遞)和回調鏈 的場景中尤其如此,這可能會導致邏輯混亂並使程式碼更難編寫維護或調試。

這些挑戰通常會創建緊密耦合的組件,降低靈活性,並增加試圖追蹤資料如何流經應用程式的開發人員的認知負擔。如果沒有更好的方法,這種複雜性會顯著減慢開發速度並導致脆弱的程式碼庫

傳統流程:Props 向下,Callbacks 向上

在典型的 React 應用程式中,父元件將 props 傳遞給子元件,子元件透過觸發回調與父元件進行通訊。這對於淺層組件樹來說效果很好,但隨著層次結構的加深,事情開始變得混亂:

Props 鑽取:資料必須透過多個層級的元件手動向下傳遞,即使只有最深層的元件需要它。

回呼鏈:同樣,子元件必須將事件處理程序沿樹向上轉發,創建緊密耦合且難以維護的結構。

一個常見問題:回呼複雜性

以此場景為例:

  • 家長將道具傳給孩子A
  • 從那裡,道具會深入到 GrandChildren A/B,並最終深入到 SubChildren N
  • 如果 SubChildren N 需要通知父級某個事件,它會觸發回調 ,該回調會透過每個中間元件 向上傳播。

隨著應用程式的成長,此設定變得更難管理。中間元件通常只是充當中間人,轉發 props 和回調,這使得程式碼變得臃腫並降低了可維護性。

Event-Driven Architecture for Clean React Component Communication

為了解決道具鑽探,我們經常求助於全域狀態管理庫(例如Zusstand)等解決方案來簡化資料共享。但是如何管理回調呢?

這就是事件驅動方法可以改變遊戲規則的地方。透過解耦組件並依靠事件來處理交互,我們可以顯著簡化回調管理。讓我們探討一下這種方法是如何運作的。


解決方案:採用事件驅動方法

Event-Driven Architecture for Clean React Component Communication

事件驅動架構不是依賴直接回調在樹上進行通信,而是事件驅動的架構解耦組件並集中通信。其工作原理如下:

事件調度

SubChildren N觸發事件(例如onMyEvent)時,它不會直接呼叫Parent中的回調。
相反,它調度由集中式事件處理程序處理的事件

集中處理

事件處理程序偵聽調度的事件並處理它。
它可以通知父級(或任何其他感興趣的組件)或根據需要觸發其他操作。

道具保持向下

道具仍然沿著層次結構傳遞,確保元件接收到它們運作所需的資料。

這個問題可以透過 zustand、redux 等集中式狀態管理工具來解決,但本文不會介紹。


執行

但是,我們要如何實現這個架構?

使用事件鉤子

讓我們建立一個名為 useEvent 的自訂鉤子,該鉤子將負責處理事件訂閱並傳回一個調度函數來觸發目標事件。

由於我使用的是 typescript,我需要擴充視窗事件介面才能建立自訂事件:

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

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

透過這樣做,我們可以定義自訂事件映射並傳遞自訂參數:

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
) => {
  ...
};

現在我們定義了需要的接口,讓我們看看最終的鉤子代碼

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

useEvent 鉤子是一個自訂的 React 鉤子,用於訂閱和分派自訂視窗事件。它允許您監聽自訂事件並使用特定的負載觸發它們。

我們在這裡所做的非常簡單,我們使用標準事件管理系統並對其進行擴展以適應我們的自訂事件。

參數:

  • eventName (string): 要監聽的事件的名稱。
  • 回調(可選):觸發事件時呼叫的函數,接收負載作為參數。

特徵:

  • 事件監聽器:它監聽指定的事件並使用事件的詳細資訊(自訂負載)呼叫提供的回調。
  • 調度事件:此鉤子提供了一個調度函數來使用自訂負載觸發事件。

例子:

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

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

好吧,很酷,但是,怎麼樣

現實世界的例子?

查看此 StackBlitz(如果未加載,請在此處查看

這個簡單的範例展示了 useEvent 掛鉤的用途,基本上,主體的按鈕正在調度從側邊欄、頁首和頁尾組件攔截的事件,並相應更新。

這讓我們可以定義因果反應,而無需將回呼傳播到許多組件。


useEvent 的真實用例

以下是一些真實用例,其中 useEvent 掛鉤可以簡化 React 應用程式中的通訊並解耦元件:


1. 通知系統

通知系統通常需要全球通訊。

  • 場景

    • 當 API 呼叫成功時,需要在應用程式中顯示「成功」通知。
    • 標題中的「通知徽章」等元件也需要更新。
  • 解決方案:使用 useEvent 掛鉤來調度帶有通知詳細資訊的 onNotification 事件。像NotificationBanner和Header這樣的元件可以監聽這個事件並獨立更新。

2. 主題切換

當使用者切換主題(例如,亮/暗模式)時,多個元件可能需要回應。

  • 場景

    • ThemeToggle 元件調度自訂 onThemeChange 事件。
    • 側邊欄和標題等元件偵聽此事件並相應地更新其樣式。
  • 好處:不需要在整個組件樹上透過 props 傳遞主題狀態或回呼函數。

3. 全域按鍵綁定

實作全域快速鍵,例如按「Ctrl S」儲存草稿或「Escape」關閉模式。

  • 場景
    • 全域 keydown 監聽器調度 onShortcutPressed 事件以及按下的按鍵詳細資訊。
    • 模態元件或其他 UI 元素回應特定的快速鍵,而不依賴父元件轉送按鍵事件。

4. 即時更新

聊天應用程式或即時儀表板等應用程式需要多個元件來對即時更新做出反應。

  • 場景
    • 當新資料到達時,WebSocket 連線會調度 onNewMessage 或 onDataUpdate 事件。
    • 聊天視窗、通知和未讀訊息計數器等元件可以獨立處理更新。

5. 跨元件的表單驗證

對於具有多個部分的複雜表單,可以集中驗證事件。

  • 場景
    • 當使用者填入欄位時,表單元件調度 onFormValidate 事件。
    • 摘要元件偵聽這些事件以顯示驗證錯誤,而不與表單邏輯緊密耦合。

6. 分析追蹤

追蹤使用者互動(例如按鈕點擊、導航事件)並將其傳送到分析服務。

  • 場景
    • 調度帶有相關詳細資訊的 onUserInteraction 事件(例如,按一下的按鈕的標籤)。
    • 中央分析處理程序偵聽這些事件並將它們傳送到分析 API。

7. 協作工具

對於共享白板或文件編輯器等協作工具,事件可以管理多用戶互動。

  • 場景
    • 每當使用者繪製、鍵入或移動物件時調度 onUserAction 事件。
    • 其他客戶端和 UI 元件監聽這些事件以即時反映變化。

透過在這些場景中利用 useEvent 掛鉤,您可以建立模組化、可維護且可擴展的應用程式,而無需依賴深度嵌套的 props 或回調鏈。


結論

事件可以透過降低複雜性和提高模組化來改變您建立 React 應用程式的方式。從小事做起——確定應用程式中的一些元件,這些元件將受益於解耦通訊並實現 useEvent 掛鉤。

透過這種方法,您不僅可以簡化程式碼,還可以使其在未來更易於維護和擴展。

為什麼要使用事件?
當您需要元件對應用程式中其他地方發生的事情做出反應,而不引入不必要的依賴項或複雜的回調鏈時,事件就會發揮作用。這種方法減少了認知負擔並避免了緊密耦合組件的陷阱。

我的推薦
使用事件進行元件間通訊-當一個元件需要通知其他元件有關操作或狀態變更時,無論它們在元件樹中的位置為何。
避免使用事件進行元件內通信,特別是對於密切相關或直接連接的元件。對於這些場景,依賴 React 的內建機制,如 props、state 或 context。

平衡的方法
雖然事件的力量很強大,但過度使用它們可能會導致混亂。明智地使用它們來簡化鬆散連接元件之間的通信,但不要讓它們取代 React 管理本地互動的標準工具。

以上是用於乾淨 React 元件通訊的事件驅動架構的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn