# useSubscription

`useSubscription` Hook 提供了一种使用 WebSocket 从支付服务订阅实时事件的简单方法。它处理连接、订阅和清理逻辑，让您可以专注于应用程序如何响应像 `invoice.paid` 这样的事件。

这对于创建动态用户体验至关重要，在这种体验中，UI 需要根据后端事件立即更新，而无需用户刷新页面。

## 工作原理

该 Hook 抽象了管理 WebSocket 连接的复杂性。当组件使用 `useSubscription` 时，它会与中继服务建立持久连接。然后，它会订阅您提供的特定 `channel`。该 Hook 会自动为您对通道进行命名空间化，构造一个格式为 `relay:<app-id>:<your-channel>` 的最终通道名称。

当后端服务向该通道发布事件时，Hook 的订阅对象会发出该事件，您的组件可以监听该事件。

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

## 参数

该 Hook 接受一个字符串参数。

| 参数 | 类型     | 描述                                                                                                                           | 是否必需 |
| :-------- | :------- | :------------------------------------------------------------------------------------------------------------------------------------ | :------- |
| `channel`   | `string` | 您想要监听的事件流的唯一标识符。**重要提示**：此字符串不得包含 `/`、`.` 或 `:` 等分隔符。 | 是      |

## 返回值

该 Hook 从底层的 `@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(() => {
    // WebSocket 连接建立后，subscription 对象才可用。
    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);
      };
    }
    // 如果 subscription 对象本身发生变化，此 effect 应重新运行。
  }, [subscription, invoiceId]);

  return (
    <div>
      <h2>Invoice Status</h2>
      <p>ID: {invoiceId}</p>
      <p>Status: <strong>{status.toUpperCase()}</strong></p>
      {status === 'pending' && <p>Waiting for payment confirmation...</p>}
      {status === 'paid' && <p>Payment confirmed! Your invoice is settled.</p>}
    </div>
  );
}

export default InvoiceStatusTracker;
```