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.
- `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 đủ.
- `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 và Khởi tạo

Phương thức `init` được thiết kế để quản lý quá trình tải dữ liệu ban đầu, điều này cực kỳ quan trọng đối với hoạt động chính xác của **Server Side Rendering (SSR)** và đảm bảo giao diện được hiển thị nhanh chóng trên client.

### Cơ chế hoạt động (Hydration)

1.  **Phía Máy chủ (SSR):**
    Khi trang được mở lần đầu tiên, hãy gọi `await init()` trong `async setup()`. Điều này buộc máy chủ phải đợi phản hồi từ API và render component với dữ liệu đã sẵn sàng. Dữ liệu nhận được sẽ tự động được đưa vào bộ nhớ đệm trạng thái (cache), sau đó được chuyển đến client.

2.  **Phía Máy khách (Hydration):**
    Khi trình duyệt "kích hoạt" (hydrate) trang, `useApiRef` trước tiên sẽ kiểm tra dữ liệu trong bộ nhớ đệm. Nếu dữ liệu có ở đó (được nhận từ máy chủ), chúng sẽ được áp dụng ngay lập tức vào `data`, tránh việc lặp lại yêu cầu API.

3.  **Điều hướng Động (Client-side Nav):**
    Nếu người dùng điều hướng đến trang một cách linh hoạt (không tải lại trang), component sẽ render ngay lập tức mà không cần đợi `init()` hoàn thành. Điều này cho phép hiển thị ngay lập tức **skeleton** hoặc chỉ báo đang tải (`loading`) trong khi dữ liệu đang được tải ở chế độ nền.
## Đố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()
```
