No.
2022-01-21
  • Jan
  • Feb
  • Mar
  • Apr
  • May
  • Jun
  • Jul
  • Aug
  • Sep
  • Oct
  • Nov
  • Dec
  • Sun
  • Mon
  • Tue
  • Wed
  • Thu
  • Fri
  • Sat
  • 27
  • 28
  • 29
  • 30
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

やったこと

リアルタイムチャット機能を追加した

react-router-domにおけるparamsの受け渡し

以前までは(v5)propsにparamsの値が渡されるようになっていたが、v6からparamsの取得にはuseParamsの使用が必須になった

React側でのsocket.ioの設定

今回はChat機能を作るためにuseChatというカスタムフックを作成する

import { useCallback, useEffect, useRef, useState } from "react";
import socketIOClient, { Socket } from "socket.io-client";
import { DefaultEventsMap } from "socket.io/dist/typed-events";

const NEW_CHAT_MESSAGE_EVENT = "newChatMessage";
const SOCKET_SERVER_URL = "http://localhost:4000";

type MessageType = {
  ownedByCurrentUser: "my-message" | "received-message";
  body: string | null;
};

export const useChat = (roomId: number) => {
  const [messages, setMessages] = useState<MessageType[]>([]);
  const socketRef = useRef<Socket<DefaultEventsMap> | null>();

  useEffect(() => {
    socketRef.current = socketIOClient(SOCKET_SERVER_URL, {
      query: { roomId },
    });

    socketRef.current.on(NEW_CHAT_MESSAGE_EVENT, (message) => {
      if (socketRef.current !== undefined && socketRef.current !== null) {
        const incomingMessage = {
          ...message,
          owndByCurrentUser: message.senderId === socketRef.current.id,
        };
        setMessages((messages) => [...messages, incomingMessage]);
      }
    });

    return () => {
      if (socketRef.current !== undefined && socketRef.current !== null) {
        socketRef.current.disconnect();
      }
    };
  }, [roomId]);

  const sendMessage = useCallback((messageBody) => {
    if (socketRef.current !== undefined && socketRef.current !== null) {
      socketRef.current.emit(NEW_CHAT_MESSAGE_EVENT, {
        body: messageBody,
        senderId: socketRef.current.id,
      });
    }
  }, []);

  return { messages, sendMessage };
};

では上から見ていく

1. queryをセットして、エンドポイントにアクセスする

socketRef.current = socketIOClient(SOCKET_SERVER_URL, {
  query: { roomId },
});

2. messageデータを受け取って、さらに最新のメッセージを追加して返すイベントハンドラを登録

socketRef.current.on(NEW_CHAT_MESSAGE_EVENT, (message: any) => {
  if (socketRef.current !== undefined && socketRef.current !== null) {
    const incomingMessage = {
      ...message,
      owndByCurrentUser: message.senderId === socketRef.current.id,
    };
    setMessages((messages) => [...messages, incomingMessage]);
  }
});

3. useEffectのクリーンアップ関数でdisconnect処理

return () => {
  if (socketRef.current !== undefined && socketRef.current !== null) {
    socketRef.current.disconnect();
  }
};

useEffect()では、副作用関数がクリーンアップ関数を返すことで、マウント時に実行した処理をアンマウント時に解除する
またその副作用関数は、毎回のレンダリング時に実行され、新しい副作用関数を実行する前に、ひとつ前の副作用処理をクリーンアップする [

4. それらを実行する関数と値を返す

const sendMessage = useCallback((messageBody: any) => {
  if (socketRef.current !== undefined && socketRef.current !== null) {
    socketRef.current.emit(NEW_CHAT_MESSAGE_EVENT, {
      body: messageBody,
      senderId: socketRef.current.id,
    });
  }
}, []);

return { messages, sendMessage };

https://user-images.githubusercontent.com/78260526/150520431-68e1dd32-f48c-483c-b70a-72901b377af3.mov