0Pricing
React Academy · レッスン

リアルタイムチャットコンポーネントの構築

WebSocket eventsとReact stateを使い、スクロール可能なチャットリストにリアルタイムのメッセージを表示します。

「リアルタイムチャットコンポーネントの構築」はCoddyKit上の無料React Academyレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはReact Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 React Academyコースには全4レッスンが含まれています。

チャットコンポーネントのアーキテクチャ

リアルタイムチャットは、メッセージ一覧(表示)、メッセージ入力(送信)、WebSocket/Socket.IO 接続レイヤーの 3 つで構成されます。ソケットのロジックはカスタムフックにまとめてください。

メッセージデータの形

チャットメッセージ用の TypeScript インターフェースを定義すると、コンポーネント全体で型安全性を保てます。

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

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

useChat フック

接続、メッセージの状態、送信関数を 1 つのフックにまとめます。

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 };
}

メッセージ一覧コンポーネント

メッセージをスクロール可能な一覧に表示します。末尾の sentinel に useRef を使用し、新しいメッセージが届いたときにそこまでスクロールします。

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>
  );
}

メッセージ入力コンポーネント

Enter キーまたはボタンのクリックで送信する制御された入力です。送信後に入力欄をクリアします。

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>
  );
}

チャット UI の組み立て

フックとサブコンポーネントを 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>
  );
}

楽観的更新

サーバーの確認前にメッセージをローカルの状態へ即座に追加し、すぐにフィードバックを返します。サーバーがエラーを返した場合は、状態を調整するかメッセージを削除します。

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));
  });
}, []);

入力中インジケーター

入力が変更されたときに(デバウンスして)typing イベントを発行し、他のユーザーの typing イベントをリッスンしてインジケーターを表示します。

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

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

メッセージのタイムスタンプ

Intl.DateTimeFormat でタイムスタンプをフォーマットすると、ロケールに応じた表示ができます。

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

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

既読通知

ユーザーがメッセージを表示したときに read イベントを発行し、サーバーからの既読確認をリッスンしてチェックマークを表示します。

仮想化されたメッセージ一覧

長いチャット履歴には、react-window または react-virtual を使用してメッセージ一覧を仮想化し、表示中の行だけをレンダーします。

メッセージ履歴の制限

長時間のセッションでメモリ使用量が際限なく増加しないよう、状態内の messages 配列に上限を設けます。

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

クイックチェック

チャット一覧に新しいメッセージが届いたとき、末尾の sentinel div までスクロールする目的は何ですか?

まとめ

ソケットのロジックをまとめた useChat フック、自動スクロールに対応した MessageList、送信時に入力をクリアする MessageInput を使って、リアルタイムチャットを構築します。すぐにフィードバックを返すには楽観的更新を使用し、メモリリークを防ぐためにメッセージ履歴に上限を設けます。

よくある質問

「リアルタイムチャットコンポーネントの構築」レッスンは無料ですか?

はい。「リアルタイムチャットコンポーネントの構築」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、React Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 React Academyコースには全4レッスンが含まれています。

「リアルタイムチャットコンポーネントの構築」で何を学びますか?

WebSocket eventsとReact stateを使い、スクロール可能なチャットリストにリアルタイムのメッセージを表示します。 ブラウザで直接実行するハンズオンコードでReact Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

React Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのReact Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。

「リアルタイムチャットコンポーネントの構築」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このReact Academyレッスンでコードを書いて実行できますか?

はい。すべてのReact Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. ブラウザで学ぶWebSocketの基礎
  2. ReactでSocket.IOを使う
  3. リアルタイムチャットコンポーネントの構築
  4. 再接続、エラー処理、クリーンアップ
← React Academyに戻る