import {Meta} from '@storybook/addon-docs/blocks'

<Meta title='@dxtmisha/vi/functional/Composables/useApiRef - Yêu cầu API'/>

# `useApiRef`

Composable để làm việc với các yêu cầu API trong Vue component. Trả về dữ liệu phản ứng, cờ trạng thái và các phương thức quản lý tải.

## Các tính năng chính
- **Khởi tạo lười (Lazy)**: Các yêu cầu và watcher phụ thuộc chỉ được khởi tạo khi truy cập lần đầu vào dữ liệu (`data` hoặc `item`). Điều này cho phép khai báo `useApiRef` mà không gây ra tải mạng hoặc CPU ngay lập tức.
- **Phạm vi toàn cục (Eternal)**: Nếu tham số `unmounted` được đặt thành `false`, tính phản ứng sẽ được chuyển sang `EffectScopeGlobal`. Điều này đảm bảo các yêu cầu tiếp tục cập nhật ngay cả sau khi component tạo ra chúng bị unmount.

**Tham số:**
- `path?: RefOrNormal<string | undefined>` — đường dẫn endpoint (có thể là `Ref` phản ứng).
- `options?: ApiOptions` — tùy chọn yêu cầu (đối tượng `ApiFetch` hoặc chuỗi tên phương thức).
- `reactivity?: boolean` — tự động chạy lại yêu cầu khi `path`, `options` hoặc ngôn ngữ thay đổi. Mặc định: `true`.
- `conditions?: RefType<boolean>` — điều kiện thực thi phản ứng. Nếu `false` — yêu cầu không được thực hiện và dữ liệu bị xóa.
- `transformation?: (data: T, isResponseContractValid?: ApiDataValidation) => ApiData<R>` — hàm chuyển đổi phản hồi trước khi lưu vào `data`. Nhận kết quả xác thực hợp đồng ở đối số thứ hai.
- `validateResponseContract?: (data: T) => ApiDataValidation` — hàm xác thực hợp đồng dữ liệu phản hồi.
- `errorContract?: ApiErrorStorageList` — nơi lưu trữ hợp đồng lỗi phản hồi.
- `unmounted?: boolean` — nếu `true` (mặc định), dữ liệu bị xóa và watcher dừng lại khi component unmount. Khi `false`, `EffectScopeGlobal` được sử dụng để duy trì chạy nền.
- `apiInstance?: ApiInstance` — Phiên bản API sử dụng cho yêu cầu. Mặc định là `Api.getItem()`.

**Trả về (`UseApiRef<R>`):**

Trạng thái phản ứng:
- `data: ComputedRef<ApiData<R> | undefined>` — Tham chiếu đến dữ liệu đã tải (Computed). Bắt đầu **khởi tạo lười** (yêu cầu và watcher) khi truy cập lần đầu.
- `item: Ref<ApiData<R> | undefined>` — Tham chiếu trực tiếp đến dữ liệu (Ref). Cũng bắt đầu khởi tạo lười khi truy cập.
- `isResponseContractValid: ComputedRef<boolean>` — `true` nếu hợp đồng phản hồi hợp lệ (trạng thái `success`).
- `responseValidationResult: ComputedRef<ApiDataValidation | undefined>` — đối tượng kết quả xác thực hợp đồng đầy đủ.
- `errorItem: ComputedRef<ApiErrorItem | undefined>` — đối tượng lỗi hiện tại nếu yêu cầu thất bại.
- `starting: ComputedRef<boolean>` — Trả về `true` nếu dữ liệu chưa bao giờ được yêu cầu hoặc đang trong quá trình lấy phần dữ liệu đầu tiên (`data` vẫn là `undefined`).
- `loading: Ref<boolean>` — Trả về `true` nếu bất kỳ yêu cầu mạng nào (ban đầu hoặc `reset` sau đó) hiện đang được thực hiện.
- `reading: Ref<boolean>` — Cờ cho quá trình đọc/xử lý dữ liệu đang hoạt động.

Phương thức:
- `isStarting(): boolean` — Phương thức đồng bộ trả về giá trị hiện tại của cờ `starting`.
- `isLoading(): boolean` — Phương thức đồng bộ trả về giá trị hiện tại của cờ `loading`.
- `isReading(): boolean` — Phương thức đồng bộ trả về giá trị hiện tại của cờ `reading`.
- `getItem(): ApiData<R> | undefined` — Phương thức để lấy giá trị hiện tại của `data` mà không tạo ra phụ thuộc phản ứng.
- `init(awaitFetch?: boolean): Promise<void>` — Phương thức để khởi tạo yêu cầu và watcher một cách rõ ràng. Tự động kích hoạt khi truy cập `data` lần đầu. Nếu `awaitFetch` là `true`, nó sẽ đợi yêu cầu đầu tiên hoàn thành.
- `reset(): Promise<void>` — Phương thức bất đồng bộ để buộc chạy lại yêu cầu. Hủy yêu cầu hiện tại (nếu có) và bắt đầu một yêu cầu mới.
- `stop(): void` — Dừng việc theo dõi các phụ thuộc phản ứng và đặt lại `data` thành `undefined`.
- `abort(): void` — Hủy yêu cầu HTTP hiện tại thông qua `AbortController` nội bộ, trong khi vẫn giữ lại dữ liệu đã tải trong `data`.

## Khởi tạo lười (Lazy Initialization)

Yêu cầu và cơ chế theo dõi thay đổi **không được bắt đầu** ngay lập tức khi gọi `useApiRef`. Chúng chỉ được kích hoạt khi truy cập `data`, `item` hoặc gọi `init()`. Điều này tối ưu hóa hiệu suất của các script setup.

## Điều kiện toàn cục

`setApiRefGlobalConditions(conditions)` — thiết lập điều kiện toàn cục cho tất cả các instance `useApiRef`. Chỉ có hiệu lực một lần.

## SSR (Server-Side Rendering)

Để đảm bảo hoạt động chính xác ở phía máy chủ (SSR), phương thức **`initSsr()`** phải được sử dụng.

### Phương thức `initSsr()`
Khi được gọi trong `setup()` của một component, nó đảm bảo dữ liệu được tải trên máy chủ trước khi render trang và được áp dụng ngay lập tức ở phía máy khách.

```typescript
const { data, initSsr } = useApiRef('path/to/api')

// Gọi phương thức này để hỗ trợ SSR
initSsr()
```

### Tự động hóa qua `AsyncRef`
Để tránh việc gọi `initSsr()` thủ công, bạn có thể sử dụng phiên bản bất đồng bộ — [**`useApiAsyncRef`**](./useApiAsyncRef.vi.mdx). Nó tự động gọi `initSsr()` khi được tạo.
## Đối tượng ApiOptions

`ApiOptions` — có thể nhận một chuỗi tên `method` (ví dụ: `'GET'`) hoặc một đối tượng kiểu `ApiFetch`. Các trường của đối tượng `ApiFetch`:

- `path?: string` — đường dẫn đến endpoint script tương đối so với URL cơ bản (ví dụ: `'users/list'`).
- `pathFull?: string` — URL đầy đủ của yêu cầu. Nếu được chỉ định, sẽ bỏ qua `api` và `path`.
- `api?: boolean` — có thêm URL cơ bản trước `path` hay không. Mặc định: `true`.
- `method?: ApiMethod` — phương thức HTTP (`'GET'`, `'POST'`, `'PUT'`, `'DELETE'`). Mặc định: `'GET'`.
- `request?: FormData | Record<string, any> | string` — thân yêu cầu (cho POST/PUT) hoặc các tham số truy vấn (cho GET). Được truyền dưới dạng JSON hoặc `FormData`.
- `headers?: Record<string, string> | null` — các tiêu đề yêu cầu bổ sung. `null` — vô hiệu hóa tất cả các tiêu đề.
- `type?: string` — giá trị `Content-Type`. Mặc định: `'application/json;charset=UTF-8'`.
- `auth?: boolean` — có thêm các tiêu đề xác thực hay không.
- `toData?: boolean` — nếu `true`, tự động giải nén `data` từ vỏ bọc `{ data: ... }`. Mặc định: `true`.
- `hideError?: boolean` — ngăn chặn việc xuất lỗi ra `console.error` khi thất bại. Mặc định: `false`.
- `globalPreparation?: boolean` — có chạy hook toàn cục `setPreparation` trước yêu cầu này hay không. Mặc định: `true`.
- `globalEnd?: boolean` — có chạy hook toàn cục `setEnd` sau phản hồi hay không. Mặc định: `true`.
- `queryReturn?: (query: Response) => Promise<any>` — trình xử lý phản hồi thay vì đọc JSON tiêu chuẩn.
- `init?: RequestInit` — các tùy chọn bổ sung cho `fetch()` gốc (CORS, chế độ cache, v.v.).
- `controller?: AbortController` — bộ điều khiển để hủy yêu cầu.
- `retry?: number` — số lần thử lại khi thất bại.

## Ví dụ

```typescript
import { ref } from 'vue'
import { useApiRef, setApiRefGlobalConditions } from '@dxtmisha/functional'

const userId = ref('123')
const isAuthenticated = ref(true)

// Điều kiện toàn cục (tất cả yêu cầu chỉ chạy nếu đã xác thực)
setApiRefGlobalConditions(isAuthenticated)

const {
  data: user,
  starting,
  loading,
  reading,
  getItem,
  reset,
  stop,
  abort
} = useApiRef(
  () => `users/${userId.value}`,
  { method: 'GET' },
  true // Tự động chạy lại khi userId thay đổi
)

// Kiểm tra trạng thái không có tính phản ứng
console.log(getItem()) // undefined trước khi truy cập data lần đầu

// Tải lại bắt buộc
await reset()

// Hủy yêu cầu (không xóa dữ liệu)
abort()

// Dừng theo dõi (xóa dữ liệu)
stop()
```
