Frontend (React + TypeScript + Vite)

В этом модуле создадим современный фронтенд на React + TypeScript + Vite для нашего чата.

Перед началом: React, TypeScript. Инструменты: Vite, React 18. Время: ~4 ч.


🔥 Инцидент. Команда пишет фронтенд на Create React App. Сборка — 40 секунд. HMR — 3 секунды. Размер бандла — 2 МБ. CRA мёртв (React team рекомендует Vite). Переход: быстрее сборка, меньше бандл, лучший DX.


📖 Теория

Фокус: современный стек с быстрым DX. Vite — мгновенный dev-сервер на ESM и сборка на Rollup/esbuild, React 18 + TypeScript strict ловят ошибки на этапе компиляции.

Инициализация проекта

bash
npm create vite@latest chat-frontend -- --template react-ts
cd chat-frontend
npm install react-router-dom

Хук для WebSocket

Создадим кастомный хук для управления WebSocket соединением:

typescript
// hooks/useChatSocket.ts
import { useEffect, useState } from 'react';

export const useChatSocket = (url: string) => {
  const [messages, setMessages] = useState<string[]>([]);
  const [ws, setWs] = useState<WebSocket | null>(null);

  useEffect(() => {
    const socket = new WebSocket(url);
    setWs(socket);

    socket.onmessage = (event) => {
      const data = JSON.parse(event.data);
      setMessages((prev) => [...prev, data.text]);
    };

    return () => socket.close();
  }, [url]);

  const sendMessage = (msg: string) => {
    if (ws && ws.readyState === WebSocket.OPEN) {
      ws.send(JSON.stringify({ action: "sendmessage", text: msg }));
    }
  };

  return { messages, sendMessage };
};

Компонент чата

typescript
// components/Chat.tsx
import { useChatSocket } from '../hooks/useChatSocket';

export function Chat() {
  const { messages, sendMessage } = useChatSocket('wss://api.example.com/ws');

  const handleSend = (e: React.FormEvent) => {
    e.preventDefault();
    const input = (e.currentTarget.elements[0] as HTMLInputElement);
    sendMessage(input.value);
    input.value = '';
  };

  return (
    <div>
      <ul>
        {messages.map((msg, i) => <li key={i}>{msg}</li>)}
      </ul>
      <form onSubmit={handleSend}>
        <input type="text" placeholder="Введите сообщение..." />
        <button type="submit">Отправить</button>
      </form>
    </div>
  );
}

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

Для сложных состояний используйте Zustand или Jotai:

bash
npm install zustand
typescript
// store/chatStore.ts
import create from 'zustand';

interface ChatState {
  messages: Message[];
  addMessage: (msg: Message) => void;
}

export const useChatStore = create<ChatState>((set) => ({
  messages: [],
  addMessage: (msg) => set((state) => ({
    messages: [...state.messages, msg]
  })),
}));

В следующем модуле превратим приложение в PWA.


💱 Компромиссы (Trade-offs)

РешениеПлюсыМинусы
Vite (ESM + Rollup/esbuild)Мгновенный dev-сервер, быстрая сборка, маленький бандлКонфиг отличается от webpack, часть плагинов не совместима
Create React AppПривычный setup, много туториаловМёртв (React team снял поддержку), медленный, тяжёлый бандл
React 18 + TypeScript strictТип-безопасность, catch ошибок в compile-timeСтрожайший онбординг, больше аннотаций
Кастомный хук useChatSocketЛогика WS инкапсулирована, переиспользуемаНет reconnect/heartbeat из коробки (см. М3)
Zustand для стейтаМинимум boilerplate, без ProviderНа масштабе — глобальные сторы (см. М4, М9)

🧩 Проверь себя

1. React 19 + TypeScript strict — зачем?
2. Кастомный хук useChatSocket управляет...
3. WebSocket reconnect с exponential backoff — зачем?
4. Vite для React+TS — главное преимущество?
5. Vite использует нативный ES modules в dev для...

🔗 Мостик к Модулю 2

Vite настроен. Спроектируем архитектуру приложения.