React Academy · Lekcja

Budowanie komponentu czatu w czasie rzeczywistym

Wyświetlanie wiadomości na żywo na przewijanej liście czatu za pomocą zdarzeń WebSocket i stanu React.

Lekcja 3 z 414 kroki

Budowanie komponentu czatu w czasie rzeczywistym to bezpłatna lekcja React Academy na CoddyKit. To lekcja 3 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej React Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs React Academy zawiera 4 lekcji w sumie.

Architektura komponentu czatu

Czat działający w czasie rzeczywistym składa się z trzech części: listy wiadomości (wyświetlanie), pola wprowadzania wiadomości (wysyłanie) oraz warstwy połączenia WebSocket/Socket.IO. Logikę gniazda należy umieścić w niestandardowym hooku.

Kształt danych wiadomości

Zdefiniuj interfejs TypeScript dla wiadomości czatu, aby zachować bezpieczeństwo typów w całym komponencie.

interface Message {
  id: string;
  text: string;
  sender: string;
  timestamp: number;
}

const [messages, setMessages] = useState<Message[]>([]);

Hook useChat

Umieść obsługę połączenia, stan wiadomości i funkcję wysyłania w jednym hooku.

function useChat(roomId: string) {
  const [messages, setMessages] = useState<Message[]>([]);

  useEffect(() => {
    socket.connect();
    socket.emit('join-room', roomId);
    socket.on('message', (msg: Message) => setMessages(m => [...m, msg]));
    return () => {
      socket.off('message');
      socket.emit('leave-room', roomId);
      socket.disconnect();
    };
  }, [roomId]);

  const sendMessage = useCallback((text: string) => {
    const msg: Message = { id: crypto.randomUUID(), text, sender: 'me', timestamp: Date.now() };
    socket.emit('message', msg);
  }, []);

  return { messages, sendMessage };
}

Komponent listy wiadomości

Renderuj wiadomości na przewijanej liście. Użyj useRef dla znacznika na dole listy i przewijaj do niego po nadejściu nowych wiadomości.

function MessageList({ messages }: { messages: Message[] }) {
  const bottomRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    bottomRef.current?.scrollIntoView({ behavior: 'smooth' });
  }, [messages]);

  return (
    <div className="message-list">
      {messages.map(msg => (
        <div key={msg.id} className={`msg ${msg.sender === 'me' ? 'mine' : 'theirs'}`}>
          <span>{msg.sender}</span>
          <p>{msg.text}</p>
        </div>
      ))}
      <div ref={bottomRef} />
    </div>
  );
}

Komponent pola wiadomości

Jest to kontrolowane pole, które emituje wiadomość po naciśnięciu Enter lub kliknięciu przycisku. Wyczyść pole po wysłaniu.

function MessageInput({ onSend }: { onSend: (text: string) => void }) {
  const [text, setText] = useState('');

  const handleSend = () => {
    if (!text.trim()) return;
    onSend(text.trim());
    setText('');
  };

  return (
    <div className="input-row">
      <input
        value={text}
        onChange={e => setText(e.target.value)}
        onKeyDown={e => e.key === 'Enter' && handleSend()}
        placeholder="Type a message..."
      />
      <button onClick={handleSend}>Send</button>
    </div>
  );
}

Składanie interfejsu czatu

Połącz hook i podkomponenty w komponent ChatRoom.

function ChatRoom({ roomId }: { roomId: string }) {
  const { messages, sendMessage } = useChat(roomId);

  return (
    <div className="chat-room">
      <h2>Room: {roomId}</h2>
      <MessageList messages={messages} />
      <MessageInput onSend={sendMessage} />
    </div>
  );
}

Optymistyczne aktualizacje

Dodaj wiadomość do stanu lokalnego natychmiast, zanim serwer ją potwierdzi, aby zapewnić natychmiastową informację zwrotną. Jeśli serwer zwróci błąd, uzgodnij stan ponownie lub usuń wiadomość.

const sendMessage = useCallback((text: string) => {
  const optimistic: Message = { id: crypto.randomUUID(), text, sender: 'me', timestamp: Date.now() };
  setMessages(m => [...m, optimistic]); // instant feedback
  socket.emit('message', optimistic, (err) => {
    if (err) setMessages(m => m.filter(msg => msg.id !== optimistic.id));
  });
}, []);

Wskaźniki pisania

Emituj zdarzenie typing przy zmianie zawartości pola (z opóźnieniem dzięki debounce) i nasłuchuj zdarzeń pisania innych osób, aby wyświetlać wskaźnik.

const emitTyping = useMemo(() => debounce(() => socket.emit('typing'), 300), []);

useEffect(() => {
  socket.on('user-typing', (user) => setTypingUser(user));
  return () => socket.off('user-typing');
}, []);

Znaczniki czasu wiadomości

Formatuj znaczniki czasu za pomocą Intl.DateTimeFormat, aby wyświetlać je zgodnie z ustawieniami lokalnymi.

function formatTime(ts: number) {
  return new Intl.DateTimeFormat('en', { hour: '2-digit', minute: '2-digit' }).format(ts);
}

<span className="timestamp">{formatTime(msg.timestamp)}</span>

Potwierdzenia odczytu

Emituj zdarzenie read, gdy użytkownik wyświetla wiadomości, i nasłuchuj potwierdzeń odczytu z serwera, aby wyświetlać znaczniki wyboru.

Wirtualizowana lista wiadomości

W przypadku długiej historii czatu użyj react-window lub react-virtual, aby zwirtualizować listę wiadomości i renderować tylko widoczne wiersze.

Ograniczanie historii wiadomości

Ogranicz tablicę wiadomości w stanie, aby zapobiec nieograniczonemu wzrostowi zużycia pamięci podczas długich sesji.

socket.on('message', (msg: Message) => {
  setMessages(prev => [...prev.slice(-499), msg]); // keep last 500
});

Szybkie sprawdzenie

Jaki jest cel przewijania do elementu div będącego znacznikiem na dole listy, gdy w czacie pojawiają się nowe wiadomości?

Podsumowanie

Zbuduj czat działający w czasie rzeczywistym za pomocą hooka useChat zawierającego logikę gniazda, komponentu MessageList z automatycznym przewijaniem oraz komponentu MessageInput, który czyści pole po wysłaniu. Używaj optymistycznych aktualizacji, aby zapewnić natychmiastową informację zwrotną, i ograniczaj historię wiadomości, aby uniknąć wycieków pamięci.

Bezpłatny start

Ucz się React dzięki korepetycjom AI — za darmo

Pisz i uruchamiaj kod w przeglądarce, otrzymuj natychmiastową pomoc od korepetytora AI dostępnego 24/7 i kontynuuj naukę w sieci lub w aplikacji.

Kursy
88
Lekcje
324

Często zadawane pytania

Czy lekcja „Budowanie komponentu czatu w czasie rzeczywistym” jest bezpłatna?

Tak — pełny tekst „Budowanie komponentu czatu w czasie rzeczywistym” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu React Academy, przejdź na CoddyKit PRO. Kurs React Academy zawiera 4 lekcji w sumie.

Co nauczysz się w „Budowanie komponentu czatu w czasie rzeczywistym”?

Wyświetlanie wiadomości na żywo na przewijanej liście czatu za pomocą zdarzeń WebSocket i stanu React. Ćwiczysz React Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć React Academy?

Nie wymagamy żadnego doświadczenia. React Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 3 z 4.

Ile czasu zajmuje lekcja „Budowanie komponentu czatu w czasie rzeczywistym”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji React Academy?

Tak. Każda lekcja React Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Podstawy WebSocket w przeglądarce
  2. Używanie Socket.IO z React
  3. Budowanie komponentu czatu w czasie rzeczywistym
  4. Ponowne łączenie, obsługa błędów i czyszczenie
← Powrót do React Academy