ホームページ >ウェブフロントエンド >jsチュートリアル >クリーンな React コンポーネント通信のためのイベント駆動型アーキテクチャ
React アプリケーションでのプロップのドリルリングとコールバック チェーンの終わりのないもつれにうんざりしていませんか?深くネストされたコンポーネント間の状態と通信の管理は、スパゲッティ コードと格闘するような感じですか?
イベント駆動型アーキテクチャは、コンポーネントの対話を簡素化し、複雑さを軽減し、アプリをより保守しやすくします。この記事では、カスタム useEvent フックを使用してコンポーネントを分離し、React アプリ全体の通信を改善する方法を説明します。
順を追って説明しましょう。まずは
現代のアプリケーション開発では、コンポーネント間の状態と通信の管理がすぐに面倒になることがあります。これは、プロップ ドリル (複数レベルのネストされたコンポーネントを介してデータを渡す必要がある場合) や コールバック チェーン を含むシナリオに特に当てはまります。これにより、ロジックが複雑になり、コードの実行が困難になる可能性があります。メンテナンスまたはデバッグ。
これらの課題により、緊密に結合されたコンポーネントが作成されることが多く、柔軟性が低下し、アプリケーション内でデータがどのように流れるかを追跡しようとする開発者にとって認知的負荷が増大します。より良いアプローチがなければ、この複雑さによって開発が大幅に遅くなり、脆弱なコードベースにつながる可能性があります。
典型的な React アプリケーションでは、親コンポーネントが子に props を渡し、子はコールバックをトリガーすることで親に通信を返します。これは浅いコンポーネント ツリーではうまく機能しますが、階層が深くなると、状況が複雑になり始めます。
プロップドリル: データは、最も深いコンポーネントでのみ必要な場合でも、複数のレベルのコンポーネントを介して手動で受け渡す必要があります。
コールバック チェーン: 同様に、子コンポーネントはイベント ハンドラーをツリーの上に転送する必要があり、密結合で保守が難しい構造を作成します。
次のシナリオを例に考えてみましょう:
アプリケーションが大きくなるにつれて、この設定の管理は難しくなります。中間コンポーネントは多くの場合、プロップとコールバックを転送する仲介者としてのみ機能するため、コードが肥大化して保守性が低下します。
プロップドリルに対処するために、データ共有を合理化するために、グローバル状態管理ライブラリ (例: Zustand) などのソリューションに頼ることがよくあります。しかし、コールバックの管理についてはどうすればよいでしょうか?
ここで、イベント駆動型のアプローチが変革をもたらす可能性があります。コンポーネントを分離し、イベントに依存してインタラクションを処理することで、コールバック管理を大幅に簡素化できます。このアプローチがどのように機能するかを見てみましょう。
ツリー上の通信に直接コールバックに依存するのではなく、イベント駆動型アーキテクチャによりコンポーネントが分離され、通信が集中化されます。仕組みは次のとおりです:
SubChildren N がイベント (例: onMyEvent) をトリガーするとき、親のコールバックは直接呼び出されません。
代わりに、集中イベント ハンドラーによって処理されるイベントをディスパッチします。
イベント ハンドラーは、ディスパッチされたイベントをリッスンして処理します。
必要に応じて、親 (またはその他の関係するコンポーネント) に通知したり、追加のアクションをトリガーしたりできます。
プロパティは引き続き階層の下に渡され、コンポーネントが機能するために必要なデータを確実に受け取ります。
これは、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 フックです。これにより、カスタム イベントをリッスンし、特定のペイロードでそれらをトリガーすることができます。
ここで行っていることは非常に単純です。標準のイベント管理システムを使用し、カスタム イベントに対応するためにそれを拡張しています。
interface AppEvent<PayloadType = unknown> extends Event { detail: PayloadType; } export const useEvent = <PayloadType = unknown>( eventName: keyof CustomWindowEventMap, callback?: Dispatch<PayloadType> | VoidFunction ) => { ... };
わかりました。でも、どうでしょうか?
この StackBlitz をチェックしてください (ロードされない場合は、ここで確認してください)
この簡単な例は useEvent フックの目的を示しています。基本的に本体のボタンは、サイドバー、ヘッダー、およびフッター コンポーネントからインターセプトされたイベントを送出し、それに応じて更新されます。
これにより、多くのコンポーネントにコールバックを伝播することなく、原因/結果反応を定義できるようになります。
ここでは、useEvent フックが通信を簡素化し、React アプリケーション内のコンポーネントを分離できる、実際の使用例をいくつか示します。
通知システムでは、グローバルなコミュニケーションが必要になることがよくあります。
シナリオ:
解決策: useEvent フックを使用して、通知の詳細を含む onNotification イベントを送出します。 NoticeBanner や Header などのコンポーネントは、このイベントをリッスンして個別に更新できます。
ユーザーがテーマ (ライト/ダーク モードなど) を切り替えると、複数のコンポーネントが応答する必要がある場合があります。
シナリオ:
利点: コンポーネント ツリー全体で props を介してテーマの状態やコールバック関数を渡す必要がありません。
「Ctrl S」を押して下書きを保存したり、「Escape」を押してモーダルを閉じるなどのグローバル ショートカットを実装します。
チャット アプリやライブ ダッシュボードなどのアプリケーションでは、リアルタイムの更新に反応するために複数のコンポーネントが必要です。
複数のセクションを持つ複雑なフォームの場合、検証イベントを一元化できます。
ユーザーの操作 (ボタンのクリック、ナビゲーション イベントなど) を追跡し、分析サービスに送信します。
共有ホワイトボードやドキュメントエディターなどの共同作業ツールの場合、イベントはマルチユーザーの対話を管理できます。
これらのシナリオで useEvent フックを活用すると、深くネストされた props やコールバック チェーンに依存せずに、モジュール式で保守可能でスケーラブルなアプリケーションを作成できます。
イベントは、複雑さを軽減し、モジュール性を向上させることで、React アプリケーションの構築方法を変革します。小規模から始めて、通信を分離することでメリットが得られるアプリ内のコンポーネントをいくつか特定し、useEvent フックを実装します。
このアプローチを使用すると、コードが簡素化されるだけでなく、将来の保守と拡張も容易になります。
イベントを使用する理由
イベントは、不要な依存関係や複雑なコールバック チェーンを導入することなく、アプリケーション内の他の場所で発生した何かにコンポーネントが反応する必要がある場合に威力を発揮します。このアプローチにより、認知負荷が軽減され、密結合コンポーネントの落とし穴が回避されます。
私のおすすめ
コンポーネント間の通信にはイベントを使用します。コンポーネント ツリー内の位置に関係なく、あるコンポーネントがアクションや状態の変化を他のコンポーネントに通知する必要がある場合に使用します。
コンポーネント内通信、特に密接に関連しているコンポーネントや直接接続されているコンポーネントの通信にイベントを使用することは避けてください。これらのシナリオでは、props、state、context などの React の組み込みメカニズムを利用します。
バランスの取れたアプローチ
イベントは強力ですが、使いすぎると混乱が生じる可能性があります。疎結合コンポーネント間の通信を簡素化するためにこれらを慎重に使用してください。ただし、ローカル インタラクションを管理するための React の標準ツールをそれらに置き換えさせないでください。
以上がクリーンな React コンポーネント通信のためのイベント駆動型アーキテクチャの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。