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

<Meta title='@dxtmisha/ru/functional/Composables/useApiRef - Работа с запросами'/>

# `useApiRef`

Composable для удобной работы с API-запросами во Vue-компонентах. Возвращает реактивные данные, флаги состояния и методы управления загрузкой.

## Особенности
- **Ленивая инициализация**: Запрос и наблюдение за зависимостями (watchers) инициализируются только при первом обращении к данным (`data` или `item`). Это позволяет объявлять `useApiRef` без немедленной нагрузки на сеть и процессор.
- **Глобальная область видимости (Eternal)**: Если параметр `unmounted` установлен в `false`, реактивность выносится в `EffectScopeGlobal`. Это гарантирует, что запросы продолжат обновляться даже после размонтирования компонента, который их создал.

**Параметры:**
- `path?: RefOrNormal<string | undefined>` — путь к endpoint (может быть реактивным `Ref`).
- `options?: ApiOptions` — опции запроса (объект `ApiFetch` или строка с именем метода).
- `reactivity?: boolean` — автоматически перезапускать запрос при изменении `path`, `options` или локали. По умолчанию: `true`.
- `conditions?: RefType<boolean>` — реактивное условие выполнения. Если `false` — запрос не выполняется, данные сбрасываются.
- `transformation?: (data: T, isResponseContractValid?: ApiDataValidation) => ApiData<R>` — функция преобразования ответа перед сохранением в `data`. Получает вторым аргументом результат валидации контракта.
- `validateResponseContract?: (data: T) => ApiDataValidation` — функция для проверки контракта данных ответа.
- `unmounted?: boolean` — если `true` (по умолчанию), данные сбрасываются и watcher останавливается при размонтировании компонента. При `false` используется `EffectScopeGlobal` для фоновой работы.
- `apiInstance?: ApiInstance` — Экземпляр API, используемый для выполнения запроса. По умолчанию `Api.getItem()`.

**Возвращает (`UseApiRef<R>`):**

Реактивные состояния:
- `data: ComputedRef<ApiData<R> | undefined>` — Ссылка на загруженные данные (Computed). Инициирует **ленивую инициализацию** (запрос и watcher) при первым обращении.
- `item: Ref<ApiData<R> | undefined>` — Прямая ссылка на данные (Ref). Также инициирует ленивую инициализацию при доступе.
- `isResponseContractValid: ComputedRef<boolean>` — `true`, если контракт ответа валиден (статус `success`).
- `responseValidationResult: ComputedRef<ApiDataValidation | undefined>` — полный объект результата валидации контракта.
- `starting: ComputedRef<boolean>` — Возвращает `true`, если данные еще ни разу не запрашивались или находятся в процессе получения первой порции данных (`data` еще `undefined`).
- `loading: Ref<boolean>` — Возвращает `true`, если в данный момент выполняется любой сетевой запрос (первичный или последующий `reset`).
- `reading: Ref<boolean>` — Флаг активного процесса чтения/обработки данных.

Методы:
- `isStarting(): boolean` — Синхронный метод, возвращающий текущее значение флага `starting`.
- `isLoading(): boolean` — Синхронный метод, возвращающий текущее значение флага `loading`.
- `isReading(): boolean` — Синхронный метод, возвращающий текущее значение флага `reading`.
- `getItem(): ApiData<R> | undefined` — Метод для получения текущего значения `data` без создания реактивной зависимости.
- `init(awaitFetch?: boolean): Promise<void>` — Метод для явной инициализации запроса и watchers. Вызывается автоматически при первом обращении к `data`. Если `awaitFetch` передано как `true`, дожидается выполнения первого запроса.
- `reset(): Promise<void>` — Асинхронный метод для принудительного перезапуска запроса. Отменяет текущий активный запрос (если есть) и инициирует новый.
- `stop(): void` — Останавливает наблюдение за реактивными зависимостями и сбрасывает `data` в `undefined`.
- `abort(): void` — Вызывает отмену текущего HTTP-запроса через внутренний `AbortController`, сохраняя при этом уже загруженные данные в `data`.

## Ленивая инициализация (Lazy Loading)

Запрос и механизмы слежения за изменениями **не запускаются** непосредственно при вызове `useApiRef`. Они активируются только при обращении к `data`, `item` или вызове `init()`. Это позволяет оптимизировать производительность setup-скриптов.

## Глобальные условия

`setApiRefGlobalConditions(conditions)` — устанавливает глобальное условие для всех экземпляров `useApiRef`. Работает только один раз.

## SSR и инициализация

Метод `init` предназначен для управления процессом первичной загрузки данных, что критически важно для корректной работы **Server Side Rendering (SSR)** и обеспечения быстрой отрисовки интерфейса на клиенте.

### Механика работы (Hydration)

1.  **На стороне сервера (SSR):**
    При первом открытии страницы необходимо вызвать `await init()` в `async setup()`. Это заставит сервер дождаться ответа от API и отрендерить компонент уже с готовыми данными. Полученные данные автоматически попадают в кэш состояния, который передается клиенту.

2.  **На стороне клиента (Hydration):**
    Когда браузер "оживляет" (гидрирует) страницу, `useApiRef` первым делом проверяет наличие данных в кэше. Если данные там есть (пришли с сервера), они мгновенно подставляются в `data`, исключая повторный запрос к API.

3.  **Динамическая навигация (Client-side Nav):**
    Если пользователь переходит на страницу динамически (без перезагрузки), компонент рендерится сразу, не дожидаясь завершения `init()`. Это позволяет мгновенно показать **скелетон (skeleton)** или индикатор загрузки (`loading`), пока данные подгружаются в фоновом режиме.
## Объект ApiOptions

`ApiOptions` — может принимать строку с названием `method` (например, `'GET'`) или объект типа `ApiFetch`. Поля объекта `ApiFetch`:

- `path?: string` — путь к endpoint-скрипту относительно базового URL (например: `'users/list'`).
- `pathFull?: string` — полный URL запроса. Если указан, игнорирует `api` и `path`.
- `api?: boolean` — добавлять ли базовый URL перед `path`. По умолчанию: `true`.
- `method?: ApiMethod` — HTTP-метод (`'GET'`, `'POST'`, `'PUT'`, `'DELETE'`). По умолчанию: `'GET'`.
- `request?: FormData | Record<string, any> | string` — тело запроса (для POST/PUT) или query-параметры (для GET). Передается как JSON или `FormData`.
- `headers?: Record<string, string> | null` — дополнительные заголовки запроса. `null` — отключить все заголовки.
- `type?: string` — значение `Content-Type`. По умолчанию: `'application/json;charset=UTF-8'`.
- `auth?: boolean` — добавить заголовки аутентификации.
- `toData?: boolean` — если `true`, автоматически разворачивает `data` из обертки `{ data: ... }`. По умолчанию: `true`.
- `hideError?: boolean` — подавляет вывод ошибок в `console.error` при сбое. По умолчанию: `false`.
- `globalPreparation?: boolean` — запускать ли глобальный хук `setPreparation` до этого запроса. По умолчанию: `true`.
- `globalEnd?: boolean` — запускать ли глобальный хук `setEnd` после ответа. По умолчанию: `true`.
- `queryReturn?: (query: Response) => Promise<any>` — обработчик ответа вместо стандартного чтения JSON.
- `init?: RequestInit` — дополнительные опции для нативного `fetch()` (CORS, режим кеша и др.).
- `controller?: AbortController` — контроллер для отмены запроса.
- `retry?: number` — количество повторных попыток при сбое.

## Пример

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

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

// Глобальное условие (все запросы выполняются только если аутентифицированы)
setApiRefGlobalConditions(isAuthenticated)

const {
  data: user,
  starting,
  loading,
  reading,
  getItem,
  reset,
  stop,
  abort
} = useApiRef(
  () => `users/${userId.value}`,
  { method: 'GET' },
  true // Реактивно перезапускать при изменении userId
)

// Проверка состояния без реактивности
console.log(getItem()) // undefined до первого обращения к data

// Принудительная перезагрузка
await reset()

// Отмена запроса (без сброса данных)
abort()

// Остановка слежения (сбрасывает данные)
stop()
```
