やったこと
リアルタイムチャット機能を追加した
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