From f804bef99040099e5e110a6721ba1c2e8b373092 Mon Sep 17 00:00:00 2001 From: Suk Woo Date: Tue, 24 Sep 2024 14:58:11 +0900 Subject: [PATCH] =?UTF-8?q?Refactor:=20#153=20=EC=9C=A0=EC=A0=80=20?= =?UTF-8?q?=EA=B2=80=EC=83=89=EC=9D=84=20=EC=9C=84=ED=95=9C=20SearchUserIn?= =?UTF-8?q?put=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EB=A6=AC=ED=8C=A9?= =?UTF-8?q?=ED=86=A0=EB=A7=81=20(#154)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Refactor: #153 전체 유저 검색 & 팀 유저 검색 axios 로직 수정 * Refactor: #153 SearchUserInput 컴포넌트 디바운스, Abort 로직 추가 * Refactor: #153 callback type 분리 * Rename: #153 파일 typo 수정 * Feat: #153 Exhaustive Check 기능 추가 --- src/components/common/SearchUserInput.tsx | 54 +++++++++++++++++++-- src/components/modal/task/ModalTaskForm.tsx | 48 +++++------------- src/services/teamService.ts | 8 +-- src/services/userService.ts | 8 +-- src/types/SearchCallbackType.tsx | 22 +++++++++ src/utils/exhaustiveCheck.ts | 3 ++ 6 files changed, 95 insertions(+), 48 deletions(-) create mode 100644 src/types/SearchCallbackType.tsx create mode 100644 src/utils/exhaustiveCheck.ts diff --git a/src/components/common/SearchUserInput.tsx b/src/components/common/SearchUserInput.tsx index 2315c8aa..382ed8bb 100644 --- a/src/components/common/SearchUserInput.tsx +++ b/src/components/common/SearchUserInput.tsx @@ -1,29 +1,75 @@ +import { useCallback, useEffect, useRef } from 'react'; import { IoSearch } from 'react-icons/io5'; +import exhaustiveCheck from '@utils/exhaustiveCheck'; + import type { SearchUser } from '@/types/UserType'; +import type { SearchCallback } from '@/types/SearchCallbackType'; type SearchInputProps = { id: string; label: string; keyword: string; + searchId: number; loading: boolean; userList: SearchUser[]; + searchCallbackInfo: SearchCallback; onKeywordChange: (event: React.ChangeEvent) => void; - onSearchKeyup: (event: React.KeyboardEvent) => void; - onSearchClick: () => void; onUserClick: (user: SearchUser) => void; }; export default function SearchUserInput({ id, label, + searchId, keyword, loading, userList, + searchCallbackInfo, onKeywordChange: handleKeywordChange, - onSearchKeyup: handleSearchKeyUp, - onSearchClick: handleSearchClick, onUserClick: handleUserClick, }: SearchInputProps) { + const debounceRef = useRef(null); + const abortControllerRef = useRef(null); + + const searchUsers = useCallback(() => { + if (debounceRef.current) clearTimeout(debounceRef.current); + if (abortControllerRef.current) abortControllerRef.current.abort(); + + abortControllerRef.current = new AbortController(); + const { signal } = abortControllerRef.current; + + const { type, searchCallback } = searchCallbackInfo; + switch (type) { + case 'ALL': + searchCallback(keyword, { signal }); + break; + case 'TEAM': + searchCallback(searchId, keyword, { signal }); + break; + case 'PROJECT': + searchCallback(searchId, keyword, { signal }); + break; + default: + exhaustiveCheck(type, '사용자 검색 범위가 올바르지 않습니다.'); + } + }, [searchCallbackInfo, searchId, keyword]); + + useEffect(() => { + if (keyword) debounceRef.current = setTimeout(() => searchUsers(), 500); + return () => { + if (debounceRef.current) clearTimeout(debounceRef.current); + if (abortControllerRef.current) abortControllerRef.current.abort(); + }; + }, [searchUsers, keyword]); + + const handleSearchClick = () => searchUsers(); + const handleSearchKeyUp = (e: React.KeyboardEvent) => { + if (e.key.toLowerCase() === 'enter') { + e.preventDefault(); + searchUsers(); + } + }; + return (