Управление состоянием
Приложение разделяет серверный и клиентский стейт по принципу:
- 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 │ │ │
└──────────┘ └──────────┘ └────────────┘