# useSubscription

`useSubscription` フックは、WebSocket を使用して決済サービスからのリアルタイムイベントを購読する簡単な方法を提供します。接続、サブスクリプション、クリーンアップロジックを処理するため、開発者は `invoice.paid` のようなイベントに対するアプリケーションの反応に集中できます。

これは、ユーザーがページを更新することなく、バックエンドイベントに応じてUIを即座に更新する必要がある、動的なユーザーエクスペリエンスを作成するために不可欠です。

## 仕組み

このフックは、WebSocket 接続管理の複雑さを抽象化します。コンポーネントが `useSubscription` を使用すると、リレーサービスへの永続的な接続が確立されます。次に、指定された特定の `channel` を購読します。フックは自動的にチャネルに名前空間を適用し、`relay:<app-id>:<your-channel>` という形式で最終的なチャネル名を構築します。

バックエンドサービスがそのチャネルにイベントを公開すると、フックのサブスクリプションオブジェクトがイベントを発行し、コンポーネントはそれをリッスンできます。

<!-- DIAGRAM_IMAGE_START:architecture:16:9:1764919317 -->
![useSubscription](assets/diagram/use-subscription-diagram-0.ja.jpg)
<!-- DIAGRAM_IMAGE_END -->

## パラメータ

このフックは、単一の文字列引数を取ります。

| パラメータ | タイプ | 説明 | 必須 |
| :-------- | :------- | :------------------------------------------------------------------------------------------------------------------------------------ | :------- |
| `channel` | `string` | リッスンしたいイベントストリームの一意の識別子。**重要**: この文字列には `/`、`.`、`:` などの区切り文字を含めないでください。 | はい |

## 戻り値

このフックは、基盤となる `@arcblock/ws` クライアントライブラリから `subscription` オブジェクトを返します。接続が確立されるまでの間、このオブジェクトは最初に `null` である場合があります。

接続が成功すると、返されたオブジェクトはイベントリスナーを管理するための以下のメソッドを提供します：

- `subscription.on(eventName, handler)`: イベントリスナーをアタッチします。
- `subscription.off(eventName, handler)`: イベントリスナーを削除します。

## 使用例

以下は、請求書のステータスをリアルタイムで追跡するコンポーネントの例です。バックエンドが請求書のチャネルで `invoice.paid` イベントをブロードキャストすると、コンポーネントのUIが自動的に更新されます。

```tsx Real-time Invoice Status Tracker icon=logos:react
import { useSubscription } from '@blocklet/payment-react';
import React, { useState, useEffect } from 'react';

/**
 * 請求書のステータスを表示し、'invoice.paid' イベントを受信したときに
 * リアルタイムで更新するコンポーネント。
 */
function InvoiceStatusTracker({ invoiceId }) {
  const [status, setStatus] = useState('pending');
  // この請求書専用のチャネルを購読する
  const subscription = useSubscription(invoiceId);

  useEffect(() => {
    // subscription オブジェクトは WebSocket 接続が確立された後に利用可能になります。
    if (subscription) {
      const handlePaymentSuccess = (eventData) => {
        console.log(`チャネル ${invoiceId} の 'invoice.paid' イベントを受信しました:`, eventData);
        // イベントに基づいてコンポーネントの状態を更新する
        setStatus('paid');
      };

      // 'invoice.paid' イベントのリスナーをアタッチする
      subscription.on('invoice.paid', handlePaymentSuccess);

      // コンポーネントがアンマウントされるとき、または subscription オブジェクトが変更されたときに
      // メモリリークを防ぐためにイベントリスナーをクリーンアップすることが重要です。
      return () => {
        subscription.off('invoice.paid', handlePaymentSuccess);
      };
    }
    // この effect は、subscription オブジェクト自体が変更された場合に再実行されるべきです。
  }, [subscription, invoiceId]);

  return (
    <div>
      <h2>Invoice Status</h2>
      <p>ID: {invoiceId}</p>
      <p>ステータス: <strong>{status.toUpperCase()}</strong></p>
      {status === 'pending' && <p>支払い確認を待っています...</p>}
      {status === 'paid' && <p>支払いが確認されました！請求書は決済済みです。</p>}
    </div>
  );
}

export default InvoiceStatusTracker;
```