Skip to content

Управление состоянием

Приложение разделяет серверный и клиентский стейт по принципу:

  • TanStack Query — серверные данные (API запросы, кэширование)
  • Zustand — клиентский стейт (UI, выбор компании, настройки)
  • WebSocket — real-time обновления, напрямую в Zustand stores

TanStack Query (серверный стейт)

Конфигурация

typescript
const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 5 * 60 * 1000,    // 5 минут
      gcTime: 30 * 60 * 1000,      // 30 минут
      retry: 2,
    },
  },
});

Паттерн использования

Запросы используют saasApi клиент и строковые ключи:

typescript
const { data: assistants } = useQuery({
  queryKey: ['assistants', companyId],
  queryFn: () => saasApi.getAssistants(companyId),
  enabled: !!companyId,
});

Инвалидация кэша

После мутаций — инвалидация связанных запросов:

typescript
const mutation = useMutation({
  mutationFn: (payload) => saasApi.createAssistant(payload),
  onSuccess: () => {
    queryClient.invalidateQueries({ queryKey: ['assistants'] });
  },
});

Zustand (клиентский стейт)

useCompanyStore

Управление выбором компании. Персистится через AsyncStorage.

typescript
interface CompanyState {
  currentCompanyId: string | null;
  companies: Company[];
  hasHydrated: boolean;
}

Методы:

  • setCurrentCompany(id) — выбрать текущую компанию
  • setCompanies(list) — обновить список компаний
  • addCompany(company) — добавить новую
  • clearCompanies() — очистить (при логауте)

Логика:

  • Автоматически выбирает первую компанию, если текущая удалена
  • При смене компании очищает данные чатов

useChatStore

Управление сообщениями и состоянием чата. Не персистится (in-memory).

typescript
interface ChatState {
  messagesByChat: Record<string, Message[]>;
  typingUsers: Record<string, string[]>;
  replyingTo: Message | null;
  sendingMessage: boolean;
}

Методы:

  • setMessages(chatId, messages) — установить сообщения чата
  • addMessage(chatId, message) — добавить новое сообщение
  • updateMessage(chatId, message) — обновить сообщение
  • removeMessage(chatId, messageId) — удалить сообщение
  • addTypingUser(chatId, userId) — индикатор набора
  • setReplyingTo(message) — ответ на сообщение
  • clearStore() — полная очистка (при смене компании)

Ограничение: максимум 500 сообщений на чат в памяти (OOM prevention).

useTheme

Управление темой (light/dark). Персистится через AsyncStorage.

WebSocket → Zustand

WebSocket события напрямую обновляют Zustand stores:

WebSocket event: message:new
  → useChatStore.getState().addMessage(chatId, message)

WebSocket event: message:updated
  → useChatStore.getState().updateMessage(chatId, message)

WebSocket event: message:deleted
  → useChatStore.getState().removeMessage(chatId, messageId)

WebSocket event: typing:start
  → useChatStore.getState().addTypingUser(chatId, userId)

WebSocket event: typing:stop
  → useChatStore.getState().removeTypingUser(chatId, userId)

Диаграмма потоков данных

┌──────────┐     REST      ┌──────────┐    cache    ┌────────────┐
│ saas-api │◄──────────────│ TanStack │────────────►│    UI      │
│ backend  │               │  Query   │             │ Components │
└──────────┘               └──────────┘             └────────────┘
     │                                                    ▲
     │ WebSocket                                          │
     ▼                                                    │
┌──────────┐    events     ┌──────────┐    state    ┌────────────┐
│ socket.io│──────────────►│  Zustand │────────────►│  Hooks     │
│  client  │               │  Stores  │             │            │
└──────────┘               └──────────┘             └────────────┘