# Работа с API, получение данных с сервера

<br />

## Получение данных в MobX сторе

Когда нам нужен серьезный контроль над получаемыми данными, не так важно кеширование, а также когда мы планируем сильное преобразование этих данных – мы используем загрузку данных в MobX сторе. MobX позволяет управлять состоянием приложения, обеспечивая реактивность и простоту в использовании.

Как правило, выглядит это следующим образом (с учетом того, что API-сервис уже реализован в слайсе `/api`):

```ts
export class UserStore {
  users: User[] = [];

  isLoading = false;
  isLoaded = false;

  constructor() {
    makeAutoObservable(this);
  }

  loadData = async (): Promise<void> => {
    try {
      this.isLoaded = false;
      this.isLoading = true;

      this.users = await userApi.getUsers();
    } catch (e) {
      throw new Error(`Error while loading users: ${e}`);
    } finally {
      this.isLoaded = true;
      this.isLoading = false;
    }
  };
}
```

Метод первичной загрузки данных конвенционально называется `loadData`. Метод подзагрузки (например, для реализации бесконечного скролла или пагинации) – это метод `loadMoreData`. Флаги состояний, такие как `isLoading` и `isLoaded`, помогают отслеживать состояние загрузки данных. Ошибки обрабатываются стандартно при помощи `try/catch` блока, что позволяет избежать сбоев в приложении и информировать пользователя о проблемах.

<br />

### Виды MobX сторов

Сторы бывают двух видов:

1. **Глобальные** – экземпляр класса импортируется и используется по всему приложению. Данные должны быть доступны в любом месте, и, как правило, они загружаются при старте приложения. Это удобно для данных, которые часто используются и не зависят от конкретного компонента.

2. **Локальные** – класс импортируется, и стор создается непосредственно в том компоненте, где он нужен. Данные локальные и умирают при анмаунте компонента. Это предпочтительный подход, так как он позволяет избежать избыточного состояния и улучшает производительность.

> Предпочтительно использовать локальные сторы везде, где это возможно, чтобы минимизировать количество глобального состояния и упростить управление данными.

<br />

## @tanstack/react-query

Когда преобразования над данными минимальны и необходимо кеширование, мы используем загрузку данных через кастомные хуки-обертки над `@tanstack/react-query`. Эта библиотека предоставляет мощные инструменты для работы с асинхронными запросами и управлением состоянием данных.

Как правило, алгоритм выглядит следующим образом (с учетом того, что API-сервис уже реализован в слайсе `/api`):

1. Создаем файл с ключами – `UsersQueryKeys.ts`:

```ts
const queryKeys = {
  users: ['users'],
  user(id: number) {
    return [...this.users, 'user', id];
  },
} as const;

export const USERS_QUERY_KEYS = Object.freeze(queryKeys);
```

2. Создаем кастомный хук – `userGetUser.ts`, в папке `/queries` рядом с API-сервисом:

```ts
import { useQuery } from '@tanstack/react-query';
import { userApi } from '@/api';

export const useGetUser = (userId: number) =>
  useQuery({
    queryKey: USERS_QUERY_KEYS.user(userId),
    queryFn: () => userApi.getUser(userId),
  });
```

3. Если вдруг необходимы **хелперы**, которые будут инвалидировать кэш (как правило, вызываются внутри сторов при сложных связях данных) или перезапрашивать данные, они должны находиться в папке `/helpers` на этом же уровне:

```ts
import { queryClient } from '@/index';
import { USERS_QUERY_KEYS } from '@/queries/UsersQueryKeys';

export const invalidateUserInCache = (userId: number): Promise<void> =>
  queryClient.invalidateQueries({ queryKey: USERS_QUERY_KEYS.user(userId) });
```

Далее этот хук используется по месту назначения в компоненте для получения данных. Использование `@tanstack/react-query` позволяет легко управлять состоянием загрузки, ошибками и кешированием, что значительно упрощает работу с асинхронными данными.
