From 7150ca87c3b074e88f8fe221ae72a7d8f6666c7b Mon Sep 17 00:00:00 2001 From: Alexander Daichendt Date: Wed, 12 Feb 2025 11:33:48 +0100 Subject: [PATCH] chore: migrate to reducer pattern instead of useState --- client/src/components/ChatMessages.tsx | 4 +-- client/src/components/ChatProvider.tsx | 30 ++++++++++++++--------- client/src/components/ConnectWithName.tsx | 9 ++++--- client/src/contexts/ChatContext.tsx | 27 +++++++++++++++++--- client/src/hooks/useWebSocket.ts | 10 ++++---- client/src/pages/Room.tsx | 6 ++--- client/src/reducers/chatReducers.ts | 28 +++++++++++++++++++++ 7 files changed, 85 insertions(+), 29 deletions(-) create mode 100644 client/src/reducers/chatReducers.ts diff --git a/client/src/components/ChatMessages.tsx b/client/src/components/ChatMessages.tsx index 76e4139..e10ff24 100644 --- a/client/src/components/ChatMessages.tsx +++ b/client/src/components/ChatMessages.tsx @@ -2,11 +2,11 @@ import React from "react"; import { useChatState } from "../contexts/ChatContext"; function ChatMessages() { - const { messages } = useChatState(); + const { state } = useChatState(); return ( <> - {messages.map((message) => ( + {state.messages.map((message) => (
{message.author.name} diff --git a/client/src/components/ChatProvider.tsx b/client/src/components/ChatProvider.tsx index f2b2349..d250e83 100644 --- a/client/src/components/ChatProvider.tsx +++ b/client/src/components/ChatProvider.tsx @@ -1,20 +1,26 @@ -import React from "react"; +import React, { useReducer } from "react"; import { ChatMessage, User } from "../../../shared"; import { ChatContext } from "../contexts/ChatContext"; +import { chatReducer } from "../reducers/chatReducers"; export function ChatProvider({ children }: { children: React.ReactNode }) { - const [messages, setMessages] = React.useState([]); - const [currentUser, setCurrentUser] = React.useState(null); + const [state, dispatch] = useReducer(chatReducer, { + messages: [], + currentUser: null, + isConnected: false, + }); - const addMessage = (message: ChatMessage) => { - setMessages((prev) => [...prev, message]); + const value = { + state, + dispatch, + actions: { + addMessage: (message: ChatMessage) => + dispatch({ type: "ADD_MESSAGE", payload: message }), + setUser: (user: User) => dispatch({ type: "SET_USER", payload: user }), + setConnected: (isConnected: boolean) => + dispatch({ type: "SET_CONNECTED", payload: isConnected }), + }, }; - return ( - - {children} - - ); + return {children}; } diff --git a/client/src/components/ConnectWithName.tsx b/client/src/components/ConnectWithName.tsx index 9f40135..7d8c3f8 100644 --- a/client/src/components/ConnectWithName.tsx +++ b/client/src/components/ConnectWithName.tsx @@ -6,7 +6,7 @@ interface ConnectProps { } function Connect({ onConnect }: ConnectProps) { - const { setCurrentUser } = useChatState(); + const { actions } = useChatState(); const [name, setName] = useState(""); function handleConnect() { @@ -14,8 +14,11 @@ function Connect({ onConnect }: ConnectProps) { return; } - // leave userId empty for now, it is server generated - setCurrentUser({ name, userId: "" }); + actions.setUser({ + name: name, + userId: "", // leave userId empty for now, it is server generated + }); + onConnect(name); } diff --git a/client/src/contexts/ChatContext.tsx b/client/src/contexts/ChatContext.tsx index b2a2b53..6049b29 100644 --- a/client/src/contexts/ChatContext.tsx +++ b/client/src/contexts/ChatContext.tsx @@ -1,17 +1,36 @@ -import { createContext, useContext } from "react"; +import { createContext, useContext, Dispatch } from "react"; import { ChatMessage, User } from "../../../shared"; -interface ChatContextType { +export type ChatState = { messages: ChatMessage[]; currentUser: User | null; + isConnected: boolean; +}; + +export type ChatAction = + | { type: "ADD_MESSAGE"; payload: ChatMessage } + | { type: "SET_USER"; payload: User } + | { type: "SET_CONNECTED"; payload: boolean } + | { type: "CLEAR_MESSAGES" }; + +export type ChatActions = { addMessage: (message: ChatMessage) => void; - setCurrentUser: (user: User) => void; + setUser: (user: User) => void; + setConnected: (isConnected: boolean) => void; +}; + +interface ChatContextType { + state: ChatState; + dispatch: Dispatch; + actions: ChatActions; } export const ChatContext = createContext(null); export const useChatState = () => { const context = useContext(ChatContext); - if (!context) throw new Error("useChat must be used within ChatProvider"); + if (!context) { + throw new Error("useChatState must be used within ChatProvider"); + } return context; }; diff --git a/client/src/hooks/useWebSocket.ts b/client/src/hooks/useWebSocket.ts index 0500cc5..3a45d2a 100644 --- a/client/src/hooks/useWebSocket.ts +++ b/client/src/hooks/useWebSocket.ts @@ -9,7 +9,7 @@ import { useChatState } from "../contexts/ChatContext"; export function useWebSocket(roomId: string) { const [ws, setWs] = useState(null); const [isConnected, setIsConnected] = useState(false); - const { addMessage, currentUser, setCurrentUser } = useChatState(); + const { state, actions } = useChatState(); // Handle incoming messages const handleMessage = useCallback( @@ -19,18 +19,18 @@ export function useWebSocket(roomId: string) { switch (message.type) { case "USER_JOINED": console.log("User joined", message.payload); - if (message.payload.user.name === currentUser?.name) { - setCurrentUser(message.payload.user); + if (message.payload.user.name === state.currentUser?.name) { + actions.setUser(message.payload.user); } break; case "CHAT_MESSAGE": - addMessage(message.payload); + actions.addMessage(message.payload); break; default: console.error("Unknown message type", message); } }, - [currentUser, setCurrentUser, addMessage], + [state.currentUser, actions], ); // Send a message to the server diff --git a/client/src/pages/Room.tsx b/client/src/pages/Room.tsx index 5036218..03b2f49 100644 --- a/client/src/pages/Room.tsx +++ b/client/src/pages/Room.tsx @@ -9,15 +9,15 @@ import { useChatState } from "../contexts/ChatContext"; function Room() { const { roomId } = useParams(); const { isConnected, connect, sendMessage } = useWebSocket(roomId ?? ""); - const { currentUser } = useChatState(); + const { state } = useChatState(); return (

Room ID: {roomId} - {currentUser && ( - User: {currentUser?.name} + {state.currentUser && ( + User: {state.currentUser?.name} )}