Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: zustand 설치 및 일부 적용 #299

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"tanstack",
"toastify",
"typeorm",
"Xmark"
"Xmark",
"zustand"
]
}
38 changes: 37 additions & 1 deletion client/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
"react-router-dom": "^6.18.0",
"react-textarea-autosize": "^8.5.3",
"react-toastify": "^9.1.3",
"socket.io-client": "^4.7.2"
"socket.io-client": "^4.7.2",
"zustand": "^4.5.2"
},
"devDependencies": {
"@types/react": "^18.2.15",
Expand Down
37 changes: 10 additions & 27 deletions client/src/contexts/RoomProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,16 @@
import { ReactNode, createContext, useEffect, useRef, useState } from 'react';
import { ReactNode, createContext, useEffect, useRef } from 'react';
import { Socket, io } from 'socket.io-client';
import { MessageInterface } from '../types/Message';
import { RoomInfoType } from '../types/RoomInfoType';
import { ProblemType } from '../types/ProblemType';
import { useLocation, useParams } from 'react-router-dom';
import { useRoomStore } from '../store/roomStore';
import { useLocation } from 'react-router-dom';

export type RoomContextType = {
isHost: boolean;
roomCode: string;
roomId: string | undefined;
inputRef: React.RefObject<HTMLTextAreaElement>;
messagesRef: React.RefObject<HTMLUListElement>;
socketRef: React.RefObject<Socket | null>;
messages: MessageInterface[];
setMessages: React.Dispatch<React.SetStateAction<MessageInterface[]>>;
roomInfo: RoomInfoType;
setRoomInfo: React.Dispatch<React.SetStateAction<RoomInfoType>>;
problems: ProblemType[];
setProblems: React.Dispatch<React.SetStateAction<ProblemType[]>>;
duration: number;
setDuration: React.Dispatch<React.SetStateAction<number>>;
};

export const RoomContext = createContext<RoomContextType>(
Expand All @@ -32,24 +23,25 @@ interface RoomProviderProps {

export const RoomProvider: React.FC<RoomProviderProps> = ({ children }) => {
const serverUrl = import.meta.env.VITE_BASE_URL;
const { setRoomInfo, setProblems, roomId } = useRoomStore();

const location = useLocation();

const isHost = location.state?.isHost;
const roomCode = location.state?.roomCode;
glowisn marked this conversation as resolved.
Show resolved Hide resolved
const roomId = useParams<{ roomId: string }>().roomId;
// const roomId = useParams<{ roomId: string }>().roomId;

const inputRef = useRef<HTMLTextAreaElement>(null);
const messagesRef = useRef<HTMLUListElement>(null);
const socketRef = useRef<Socket | null>(null);

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

const [roomInfo, setRoomInfo] = useState<RoomInfoType>({} as RoomInfoType);
// const [roomInfo, setRoomInfo] = useState<RoomInfoType>({} as RoomInfoType);

const [problems, setProblems] = useState<ProblemType[]>([]);
// const [problems, setProblems] = useState<ProblemType[]>([]);

const [duration, setDuration] = useState<number>(0); // minutes
// const [duration, setDuration] = useState<number>(0); // minutes

useEffect(() => {
const socket: Socket = io(serverUrl, {
Expand All @@ -74,25 +66,16 @@ export const RoomProvider: React.FC<RoomProviderProps> = ({ children }) => {
return () => {
socket.disconnect();
};
}, [roomId, serverUrl]);
}, [roomId, serverUrl, setProblems, setRoomInfo]);

return (
<RoomContext.Provider
value={{
isHost,
roomCode,
roomId,
inputRef,
messagesRef,
socketRef,
messages,
setMessages,
roomInfo,
setRoomInfo,
problems,
setProblems,
duration,
setDuration,
}}>
{children}
</RoomContext.Provider>
Expand Down
8 changes: 5 additions & 3 deletions client/src/pages/Room/Chat/Chat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@ import {
} from '../../../types/Message';
import { useRoom } from '../../../hooks/useRoom';
import { useEffect } from 'react';
import { useRoomStore } from '../../../store/roomStore';

export default function Chat() {
const { user } = useAuthContext();
const { roomId, messages, setMessages, inputRef, messagesRef, socketRef } =
const {roomId, messages, setMessages} = useRoomStore();
const { inputRef, messagesRef, socketRef } =
useRoom();

const socket = socketRef.current;
Expand Down Expand Up @@ -80,8 +82,8 @@ export default function Chat() {
}

socket?.on('chat-message', (newMessage) => {
setMessages((prevMessages) => {
const newMessages = [...prevMessages, newMessage];
setMessages((prevMessages: MessageInterface[]) => {
const newMessages: MessageInterface[] = [...prevMessages, newMessage];
setLocalStorageItem(
`${roomId}-messages`,
JSON.stringify({
Expand Down
4 changes: 2 additions & 2 deletions client/src/pages/Room/Chat/Message.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { useRoom } from '../../../hooks/useRoom';
import { userColors } from '../../../../public/userColors';
import { hash } from '../../../utils/hash';
import { ChatEvent, MessageInterface } from '../../../types/Message';
import MessageBody from './MessageBody';
import { useRoomStore } from '../../../store/roomStore';

export default function Message({ message }: { message: MessageInterface }) {
const { roomCode } = useRoom();
const { roomCode } = useRoomStore();
const generateRandomColor = (username: string) => {
const idx = Math.abs(hash(username + roomCode) % userColors.length);
return userColors[idx];
Expand Down
11 changes: 4 additions & 7 deletions client/src/pages/Room/Problems/Problems.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ import { FaPencil } from 'react-icons/fa6';

import RoomSettingModal from './RoomSettingModal/RoomSettingModal';
import { useTheme } from '../../../hooks/useTheme';
import { useRoom } from '../../../hooks/useRoom';
import ProblemLists from './ProblemLists';
import { useRoomStore } from '../../../store/roomStore';
import { useRoom } from '../../../hooks/useRoom';

export default function Problems() {
const { isHost, roomInfo, problems, setProblems, duration, setDuration } =
useRoom();
const { isHost } = useRoom();
const {roomInfo, problems } = useRoomStore();
const { theme } = useTheme();

const [isModalOpen, setIsModalOpen] = useState(false);
Expand Down Expand Up @@ -57,10 +58,6 @@ export default function Problems() {
modalOverlayRef={modalOverlayRef}
closeModal={closeModal}
modalOutsideClick={modalOutsideClick}
problems={problems}
setProblems={setProblems}
duration={duration}
setDuration={setDuration}
/>
)}
</>
Expand Down
4 changes: 2 additions & 2 deletions client/src/pages/Room/Problems/RoomSettingModal/Dropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export interface DropdownProps<T> {
options: Array<T>;
optionPostFix?: string;
selected: T;
setSelected: React.Dispatch<React.SetStateAction<T>>;
setSelected: (value: T) => void | React.Dispatch<React.SetStateAction<T>>;
buttonClassName?: string;
itemBoxClassName?: string;
itemClassName?: string;
Expand All @@ -17,7 +17,7 @@ export interface DropdownProps<T> {
* @param {Array<T>} props.options dropdown items with value
* @param {string} props.optionPostFix postfix of dropdown item
* @param {T} props.selected selected item
* @param {React.Dispatch<React.SetStateAction<T>>} props.setSelected set selected item
* @param {function(T):void | React.Dispatch<React.SetStateAction<T>> } props.setSelected set selected item
* @param {string} props.buttonClassName className of dropdown button for tailwindcss
* @param {string} props.itemBoxClassName className of dropdown item box for tailwindcss
* @param {string} props.itemClassName className of dropdown item for tailwindcss
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,20 @@
import { RefObject, useState, Dispatch, SetStateAction } from 'react';
import { useState, RefObject } from 'react';
import { FaXmark } from 'react-icons/fa6';
import { FaToggleOff } from 'react-icons/fa6';
import { FaToggleOn } from 'react-icons/fa6';
import { useShallow } from 'zustand/react/shallow';

import SelectProblem from './SelectProblem';
import RandomProblem from './RandomProblem';
import ProblemList from './ProblemList';
import { FaXmark } from 'react-icons/fa6';
import { FaToggleOff } from 'react-icons/fa6';
import { FaToggleOn } from 'react-icons/fa6';
import Dropdown from './Dropdown';
import { ProblemType } from '../../../../types/ProblemType';
import { useRoomStore } from '../../../../store/roomStore';

interface RoomSettingModalProps {
modalOverlayRef: RefObject<HTMLDivElement>;
closeModal: () => void;
modalOutsideClick: (arg: React.MouseEvent<HTMLDivElement>) => void;
problems: ProblemType[];
setProblems: Dispatch<SetStateAction<ProblemType[]>>;
duration: number;
setDuration: Dispatch<SetStateAction<number>>;
}

const iconStyle = {
Expand All @@ -27,11 +25,15 @@ export default function RoomSettingModal({
modalOverlayRef,
closeModal,
modalOutsideClick,
problems,
setProblems,
duration,
setDuration,
}: RoomSettingModalProps) {
const { problems, setProblems, duration, setDuration } = useRoomStore(
useShallow((state) => ({
problems: state.problems,
setProblems: state.setProblems,
duration: state.duration,
setDuration: state.setDuration,
})),
);
const [problem, setProblem] = useState<ProblemType>({} as ProblemType);
const [problemList, setProblemList] = useState<ProblemType[]>(problems);
const [isRandom, setIsRandom] = useState<boolean>(false);
Expand Down
2 changes: 1 addition & 1 deletion client/src/pages/Room/Room.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Problems from './Problems/Problems';
import ScoreboardButton from './ScoreBoard/ScoreBoardButton';
import Chat from './Chat/Chat';
import StartButton from './Tiimer/StartButton';
import StartButton from './Timer/StartButton';
import RoomInfo from './RoomInfo/RoomInfo';

export default function Room() {
Expand Down
4 changes: 2 additions & 2 deletions client/src/pages/Room/RoomInfo/RoomInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import ExitButton from './ExitButton';
import { FaRegCopy } from 'react-icons/fa6';
import { ToastContainer, toast } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import { useRoom } from '../../../hooks/useRoom';
import { useRoomStore } from '../../../store/roomStore';

export default function RoomInfo() {
const { roomCode } = useRoom();
const { roomCode } = useRoomStore();

const copyToClipboard = () => {
navigator.clipboard.writeText(roomCode);
Expand Down
4 changes: 2 additions & 2 deletions client/src/pages/Room/ScoreBoard/ScoreBoard.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useRoom } from '../../../hooks/useRoom';
import { useRoomStore } from '../../../store/roomStore';
import { ScoreBoardInformation } from '../../../types/ScoreBoardInformation';
import { Submission } from '../../../types/Submission';
import Players from './Players';
Expand All @@ -13,7 +13,7 @@ interface PlayerScore {
}

export default function ScoreBoard({ scores }: ScoreBoardProps) {
const { participantNames, problems } = useRoom().roomInfo;
const { participantNames, problems } = useRoomStore().roomInfo;

const playerNames: string[] = participantNames;
const problemIds: number[] = problems.map((problem) => problem.bojProblemId);
Expand Down
4 changes: 2 additions & 2 deletions client/src/pages/Room/ScoreBoard/ScoreBoardButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ import { FaChartSimple } from 'react-icons/fa6';
import ScoreBoardModal from './ScoreBoardModal';
import { useRef, useState } from 'react';
import { getScoreBoardInformation } from '../../../apis/scoreBoard';
import { useRoom } from '../../../hooks/useRoom';
import { ScoreBoardInformation } from '../../../types/ScoreBoardInformation';
import { useRoomStore } from '../../../store/roomStore';

export default function ScoreboardButton() {
const [isModalOpen, setIsModalOpen] = useState(false);
const [scores, setScores] = useState<ScoreBoardInformation>({submissions: [], rankings: []});
const modalOverlayRef = useRef<HTMLDivElement>(null);
const { roomCode, roomInfo } = useRoom();
const { roomCode, roomInfo } = useRoomStore();

const openModal = async () => {
try {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useRoom } from '../../../hooks/useRoom';
import { useRoomStore } from '../../../store/roomStore';
import Timer from './Timer';

export type StartButtonProps = {
Expand All @@ -7,7 +8,8 @@ export type StartButtonProps = {
};

export default function StartButton() {
const { socketRef, isHost, roomInfo, problems, duration } = useRoom();
const { isHost, socketRef } = useRoom();
const { roomInfo, problems, duration } = useRoomStore();

const start = () => {
console.log('start');
Expand Down
21 changes: 21 additions & 0 deletions client/src/store/roomStore.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { create } from 'zustand';
import { RoomInfoType } from '../types/RoomInfoType';
import { ProblemType } from '../types/ProblemType';
import { RoomStoreType } from '../types/RoomStoreType';

export const useRoomStore = create<RoomStoreType>((set) => ({
roomId: '',
messages: [],
setMessages: (messages) =>
set((state) => ({
...state,
messages:
typeof messages === 'function' ? messages(state.messages) : messages,
})),
roomInfo: {} as RoomInfoType,
setRoomInfo: (roomInfo: RoomInfoType) => set({ roomInfo }),
problems: [],
setProblems: (problems: ProblemType[]) => set({ problems }),
duration: 0,
setDuration: (duration: number) => set({ duration }),
}));
Loading