---
title: useProducts
description: 取得產品列表與價格的 React Hook
---

# useProducts

`useProducts` 在元件掛載時自動取得產品列表，並管理 loading 和 error 狀態。

  需要在 `RecurProvider` 內使用。

## 基本用法

```tsx
import { useProducts } from 'recur-tw';

function PricingPage() {
  const { data: products, isLoading, error } = useProducts();

  if (isLoading) return <div>載入中...</div>;
  if (error) return <div>載入失敗：{error.message}</div>;

  return (
    <div className="grid grid-cols-3 gap-4">
      {products?.map((product) => (
        <div key={product.id} className="border p-4 rounded">
          <h3>{product.name}</h3>
          <p className="text-2xl font-bold">NT${product.price}/月</p>
          <p>{product.description}</p>
        </div>
      ))}
    </div>
  );
}
```

## Options

| 參數 | 類型 | 預設 | 說明 |
|------|------|------|------|
| `type` | string | 全部 | 篩選產品類型：`'SUBSCRIPTION'`、`'ONE_TIME'`、`'CREDITS'`、`'DONATION'` |
| `enabled` | boolean | `true` | 設為 `false` 可延遲載入 |
| `onSuccess` | `(products) => void` | - | 載入成功 callback |
| `onError` | `(error) => void` | - | 載入失敗 callback |

### 篩選訂閱型產品

```tsx
const { data: plans } = useProducts({ type: 'SUBSCRIPTION' });
```

### 延遲載入

```tsx
const [ready, setReady] = useState(false);
const { data } = useProducts({ enabled: ready });

// 之後觸發載入
<button onClick={() => setReady(true)}>顯示方案</button>
```

## Return 值

| 屬性 | 類型 | 說明 |
|------|------|------|
| `data` | `Product[] \| null` | 產品列表 |
| `isLoading` | boolean | 載入中 |
| `error` | `Error \| null` | 錯誤物件 |
| `refetch` | `() => Promise<void>` | 重新載入 |

## Product 物件

```typescript
interface Product {
  id: string;
  name: string;
  slug: string;
  description?: string;
  price: number;              // TWD 整數（如 499）
  interval?: 'month' | 'year';
  intervalCount?: number;
  type: 'SUBSCRIPTION' | 'ONE_TIME' | 'CREDITS' | 'DONATION';
  trialDays?: number;
  active: boolean;
  metadata?: Record<string, unknown>;
}
```

  `price` 是 TWD 整數，不需要除以 100。`499` 就是 NT$499。

## usePlans（別名）

`usePlans` 是 `useProducts({ type: 'SUBSCRIPTION' })` 的語法糖：

```tsx
import { usePlans } from 'recur-tw';

// 等同於 useProducts({ type: 'SUBSCRIPTION' })
const { data: plans } = usePlans();
```

## 搭配 useSubscribe

```tsx
import { useProducts, useSubscribe } from 'recur-tw';

function PricingPage() {
  const { data: products, isLoading } = useProducts({ type: 'SUBSCRIPTION' });
  const { subscribe, isLoading: isCheckingOut } = useSubscribe();

  if (isLoading) return <div>載入中...</div>;

  return (
    <div>
      {products?.map((product) => (
        <button
          key={product.id}
          disabled={isCheckingOut}
          onClick={() => subscribe({
            productId: product.id,
            customerEmail: 'user@example.com',
          })}
        >
          {product.name} - NT${product.price}/{product.interval === 'month' ? '月' : '年'}
        </button>
      ))}
    </div>
  );
}
```
