Skip to content

Commit

Permalink
술 상세페이지 진입시 히스토리에 추가 (#64)
Browse files Browse the repository at this point in the history
* New : 히스토리 관련 로컬스토리지 키 타입 추가

* New : 로컬스토리지 관리 Hooks 추가

* Refactor : 히스토리 관리 로직 처리 Hooks 로 분리

* Refactor : 스켈레톤 처리 List 컴포넌트로 위임

* Refactor : Namtag 컴포넌트 Presentational component 화

* New : 위키 디테일로 이동하는 함수 Hooks 으로 변경

* Minor : 사용하지 않는 모듈 제거, FIXME 주석 추가
  • Loading branch information
jobkaeHenry authored Dec 1, 2023
1 parent 91064a8 commit 7ad4678
Show file tree
Hide file tree
Showing 11 changed files with 185 additions and 71 deletions.
35 changes: 10 additions & 25 deletions client/src/components/SearchHistory.tsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,19 @@
import { Button, Stack, StackProps, Typography } from "@mui/material";
import { useCallback, useState } from "react";
import XIcon from "@/assets/icons/XIcon.svg";
import { SearchHistoryKeyType } from "@/types/LocalStorageKey";
import useSearchHistory from "@/hooks/searchHistory/useSearchHistory";

interface SearchHistoryProps extends Omit<StackProps, "onClick"> {
storageKey: string;
onClick: () => void;
storageKey: SearchHistoryKeyType;
onClick: (keyword: string) => void;
}

const SearchHistory = ({ storageKey, onClick }: SearchHistoryProps) => {
const getItems = useCallback(() => {
return JSON.parse(localStorage.getItem(storageKey) ?? "[]") as string[];
}, [storageKey]);

const [searchHistory, setSearchHistory] = useState<string[]>(getItems());

const removeAll = useCallback(() => {
localStorage.setItem(storageKey, "[]");
setSearchHistory(getItems());
}, [storageKey]);

const removeByKeyword = useCallback(
(keyword: string) => {
const filteredHistory = searchHistory.filter(
(prevKeyword) => prevKeyword !== keyword
);
localStorage.setItem(storageKey, JSON.stringify(filteredHistory));
setSearchHistory(getItems());
},
[storageKey]
);
const {
state: searchHistory,
removeAll,
removeByKeyword,
} = useSearchHistory(storageKey);

return searchHistory.length > 0 ? (
<>
Expand All @@ -45,7 +30,7 @@ const SearchHistory = ({ storageKey, onClick }: SearchHistoryProps) => {
<Stack
key={keyword}
component="li"
onClick={onClick}
onClick={() => onClick(keyword)}
direction="row"
justifyContent="space-between"
alignItems="center"
Expand Down
1 change: 1 addition & 0 deletions client/src/components/newpost/SearchAlcoholInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ const SearchAlcoholInput = ({ setAlcoholNo }: SearchAlcoholInputInterface) => {
onBlur={() => setIsSearchingAlCohol(false)}
autoComplete="off"
/>
{/* FIXME List 컴포넌트로 분리 */}
{isSearchingAlcohol && (
<Box sx={WrapperStyle}>
<List sx={ListStyle}>
Expand Down
44 changes: 29 additions & 15 deletions client/src/components/wiki/AlcoholList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,41 @@ import AlcoholNameTag from "@/components/wiki/AlcoholNameTag";
import { AlcoholDetailInterface } from "@/types/alcohol/AlcoholInterface";
import { Typography } from "@mui/material";
import { memo } from "react";
import AlcoholListSkeleton from "./AlcoholListSkeleton";

const AlcoholList = ({
data: alcohols,
}: {
data: AlcoholDetailInterface[];
}) => {
return (
interface AlcoholList {
data?: AlcoholDetailInterface[];
onClickElement?: (data: AlcoholDetailInterface) => void;
}
/**
* 술 정보 Array 를 입력받아 List로 맵핑해주는 컴포넌트
* onClickElement 속성으로 각 엘리먼트 클릭 시 속성을 지정가능
* @returns
*/
const AlcoholList = ({ data: alcohols, onClickElement }: AlcoholList) => {
return alcohols ? (
<>
{alcohols?.length > 0 ? (
alcohols.map(({ alcoholName, alcoholNo, alcoholType }) => (
<AlcoholNameTag
key={alcoholNo}
alcoholName={alcoholName}
alcoholType={alcoholType}
alcoholNo={alcoholNo}
/>
))
{alcohols.length > 0 ? (
alcohols.map((alcohol) => {
const { alcoholName, alcoholNo, alcoholType } = alcohol;
return (
<AlcoholNameTag
key={alcoholNo}
alcoholName={alcoholName}
alcoholType={alcoholType}
alcoholNo={alcoholNo}
onClick={() => {
onClickElement && onClickElement(alcohol);
}}
/>
);
})
) : (
<Typography textAlign="center">검색 결과가 없어요</Typography>
)}
</>
) : (
<AlcoholListSkeleton disableTimer />
);
};
export default memo(AlcoholList);
10 changes: 2 additions & 8 deletions client/src/components/wiki/AlcoholNameTag.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@ import { Box, BoxProps, Chip, IconButton, Typography } from "@mui/material";
import PostSeeMoreIcon from "@/assets/icons/PostSeeMoreIcon.svg";
import { AlcoholDetailInterface } from "@/types/alcohol/AlcoholInterface";
import XIcon from "@/assets/icons/XIcon.svg";
import { useRouter } from "next/navigation";
import { WIKI_DETAIL } from "@/const/clientPath";

interface AlcoholNameTagInterface extends BoxProps {
export interface AlcoholNameTagInterface extends BoxProps {
alcoholName: AlcoholDetailInterface["alcoholName"];
alcoholType: AlcoholDetailInterface["alcoholType"];
alcoholNo: AlcoholDetailInterface["alcoholNo"];
Expand All @@ -21,7 +19,6 @@ const AlcoholNameTag = ({
onClickRemove,
...others
}: AlcoholNameTagInterface) => {
const router = useRouter();
return (
<Box sx={WrapperStyle} {...others}>
<Box
Expand Down Expand Up @@ -51,10 +48,7 @@ const AlcoholNameTag = ({
<XIcon />
</IconButton>
) : (
<IconButton
sx={{ p: 0 }}
onClick={() => router.push(WIKI_DETAIL(String(alcoholNo)))}
>
<IconButton sx={{ p: 0 }}>
<PostSeeMoreIcon style={{ margin: "3px 0" }} />
</IconButton>
)}
Expand Down
15 changes: 7 additions & 8 deletions client/src/components/wiki/AlcoholPagination.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,18 @@
import useGetAlcoholListQuery from "@/queries/alcohol/useGetAlcoholListQuery";
import AlcoholList from "@/components/wiki/AlcoholList";
import { Pagination, Stack } from "@mui/material";
import AlcoholListSkeleton from "@/components/wiki/AlcoholListSkeleton";
import usePushToWikiDetail from "@/hooks/wiki/usePushToWikiDetail";

const AlcoholPagenation = () => {
const { data: alcohols, isSuccess } = useGetAlcoholListQuery();

const { data: alcohols } = useGetAlcoholListQuery();
const onClickElementHandler = usePushToWikiDetail();
return (
<Stack alignItems="center" gap={2}>
<Stack gap={1} alignItems="center" width={"100%"} height={"232px"}>
{isSuccess ? (
<AlcoholList data={alcohols?.list} />
) : (
<AlcoholListSkeleton disableTimer />
)}
<AlcoholList
data={alcohols?.list}
onClickElement={onClickElementHandler}
/>
</Stack>
<Pagination count={alcohols?.totalCount} />
</Stack>
Expand Down
4 changes: 1 addition & 3 deletions client/src/components/wiki/searchDrawer/WikiSearchDrawer.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { SwipeableDrawer, Stack, styled, Box } from "@mui/material";
import { Stack } from "@mui/material";
import { useContext } from "react";
import WikiPageContext from "@/store/wiki/WikiPageContext";
import WikiSerachArea from "@/components/wiki/searchDrawer/WikiSerachArea";
Expand All @@ -7,7 +7,6 @@ import CustomSwipeableDrawer from "@/components/CustomSwipeableDrawer";
const WikiSearchDrawer = () => {
const { isSearching, setIsSearching } = useContext(WikiPageContext);


return (
<CustomSwipeableDrawer
open={isSearching}
Expand All @@ -22,4 +21,3 @@ const WikiSearchDrawer = () => {
};

export default WikiSearchDrawer;

29 changes: 17 additions & 12 deletions client/src/components/wiki/searchDrawer/WikiSerachArea.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
import { useEffect, useRef, useState } from "react";
import { useContext, useEffect, useRef, useState } from "react";
import useDebounce from "@/hooks/useDebounce";
import InputSearchIcon from "@/assets/icons/InputSearchIcon.svg";
import { Stack, TextField } from "@mui/material";
import useGetAlcoholListQuery from "@/queries/alcohol/useGetAlcoholListQuery";
import AlcoholList from "@/components/wiki/AlcoholList";
import AlcoholListSkeleton from "../AlcoholListSkeleton";
import SearchHistory from "@/components/SearchHistory";
import { ALCOHOL_SEARCH_HISTORY } from "@/const/localstorageKey";
import WikiPageContext from "@/store/wiki/WikiPageContext";
import usePushToWikiDetail from "@/hooks/wiki/usePushToWikiDetail";

const WikiSerachArea = () => {
const { setIsSearching } = useContext(WikiPageContext);

const [searchKeyword, setSearchKeyword] = useState("");
const debouncedValue = useDebounce(searchKeyword, 300);
const { data: alcohols, isSuccess } = useGetAlcoholListQuery(debouncedValue);
const inputRef = useRef<HTMLInputElement>(null);
const { data: alcohols } = useGetAlcoholListQuery(debouncedValue);

const onClickElementHandler = usePushToWikiDetail();

const inputRef = useRef<HTMLInputElement>(null);
useEffect(() => {
inputRef.current?.focus();
}, []);
Expand All @@ -37,17 +42,17 @@ const WikiSerachArea = () => {
<Stack gap={1} height={"232px"}>
{searchKeyword ? (
// 입력중인 경우
<>
{isSuccess ? (
<AlcoholList data={alcohols.list} />
) : (
<AlcoholListSkeleton />
)}
</>
<AlcoholList
data={alcohols?.list}
onClickElement={(alcoholData) => {
onClickElementHandler(alcoholData);
setIsSearching(false);
}}
/>
) : (
// 입력이 없는경우 검색기록 표출
<SearchHistory
onClick={() => console.log("눌림")}
onClick={(keyword) => setSearchKeyword(keyword)}
storageKey={ALCOHOL_SEARCH_HISTORY}
/>
)}
Expand Down
33 changes: 33 additions & 0 deletions client/src/hooks/localStorage/useLocalStorage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { useCallback, useEffect, useState } from "react";

const useLocalStorage = <T>(storageKey: string) => {
/**
* 로컬스토리지에 아이템을 stringify해 저장하는 함수
*/
const setItem = useCallback(
(keyword: T) => {
localStorage.setItem(storageKey, JSON.stringify(keyword));
},
[storageKey]
);
/**
* 로컬 스토리지 아이템을 파싱해서 리턴하는 함수
*/
const getItems = useCallback((): T | null => {
return JSON.parse(localStorage.getItem(storageKey) || "null");
}, [storageKey]);

const [storageValue, setStorageValue] = useState<T | null>(getItems());

// 새로운 값이 저장될 경우, 로컬스토리지에도 같이 저장
useEffect(() => {
if (!storageValue) {
return;
}
setItem(storageValue);
}, [storageValue]);

return [storageValue, setStorageValue] as const;
};

export default useLocalStorage;
54 changes: 54 additions & 0 deletions client/src/hooks/searchHistory/useSearchHistory.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { SearchHistoryKeyType } from "@/types/LocalStorageKey";
import { useCallback, useEffect } from "react";
import useLocalStorage from "../localStorage/useLocalStorage";

/**
* 로컬스토리지 키를 입력받아
* 해당 스토리지를 바라보는 state를 리턴 (State 업데이트시 자동으로 반영)
*
* @param storageKey 로컬스토리지 키
* @returns
*/
const useSearchHistory = (storageKey: SearchHistoryKeyType) => {
const [searchHistory, setSearchHistory] =
useLocalStorage<string[]>(storageKey);

useEffect(() => {
if (searchHistory === null) {
setSearchHistory([]);
}
}, []);

const removeAll = useCallback(() => {
setSearchHistory([]);
}, [storageKey]);

const removeByKeyword = useCallback(
(keyword: string) => {
const filteredHistory = (searchHistory ?? []).filter(
(prevKeyword) => prevKeyword !== keyword
);
setSearchHistory(filteredHistory);
},
[storageKey]
);
const addSearchHistory = useCallback(
(keyword: string) => {
setSearchHistory((prev) => {
return [
keyword,
...(prev ?? []).filter((prevKeyword) => prevKeyword !== keyword),
];
});
},
[storageKey]
);
return {
state: searchHistory ?? [],
add: addSearchHistory,
removeAll,
removeByKeyword,
};
};

export default useSearchHistory;
27 changes: 27 additions & 0 deletions client/src/hooks/wiki/usePushToWikiDetail.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { AlcoholDetailInterface } from "@/types/alcohol/AlcoholInterface";
import { useRouter } from "next/navigation";
import { useCallback } from "react";
import useSearchHistory from "../searchHistory/useSearchHistory";
import { ALCOHOL_SEARCH_HISTORY } from "@/const/localstorageKey";
import { WIKI_DETAIL } from "@/const/clientPath";
/**
* 검색히스토리에 해당 술을 남기고, 디테일페이지로 이동시키는 함수를 리턴하는 훅
* @returns 해당 callback함수
*/
const usePushToWikiDetail = () => {
const { add: addToSearchHistory } = useSearchHistory(ALCOHOL_SEARCH_HISTORY);
const router = useRouter();
/**
* 검색히스토리에 해당 술을 남기고, 디테일페이지로 이동시키는 함수를 리턴하는 함수
*/
const onClickElementHandler = useCallback(
({ alcoholName, alcoholNo }: AlcoholDetailInterface) => {
addToSearchHistory(alcoholName);
router.push(WIKI_DETAIL(String(alcoholNo)));
},
[addToSearchHistory]
);
return onClickElementHandler;
};

export default usePushToWikiDetail;
4 changes: 4 additions & 0 deletions client/src/types/LocalStorageKey.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/**
* 검색 기록 관련 키 타입
*/
export type SearchHistoryKeyType = "alcohol-search-history";

0 comments on commit 7ad4678

Please sign in to comment.