-
Notifications
You must be signed in to change notification settings - Fork 6
Feat: 페이지네이션 merge 이후 Link 페이지 개선, useViewport useFetchLinks 훅 추가 #91
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
The head ref may contain hidden characters: "feature/Link-\uD398\uC774\uC9C0"
Changes from 11 commits
7dd4754
4055f2b
6eaaace
68874c1
db99170
ba25f72
729e5a5
b00868b
866a0f4
9f10236
d63c126
8230c3f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| import { FolderData } from "@/types/folderTypes"; | ||
| import useModalStore from "@/store/useModalStore"; | ||
| import useRerenderFolderList from "@/hooks/useRerenderFolderList"; | ||
|
|
||
| interface AddFolderButtonProps { | ||
| setFolderList: React.Dispatch<React.SetStateAction<FolderData[]>>; | ||
| } | ||
|
|
||
| export const AddFolderButton = ({ setFolderList }: AddFolderButtonProps) => { | ||
| const { isOpen, openModal } = useModalStore(); | ||
|
|
||
| useRerenderFolderList(isOpen, setFolderList); | ||
|
|
||
| return ( | ||
| <button | ||
| className="w-[79px] h-[19px] text-purple100" | ||
| onClick={() => openModal("AddFolderModal")} | ||
| > | ||
| 폴더 추가 + | ||
| </button> | ||
| ); | ||
| }; | ||
| export default AddFolderButton; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| import { FolderData } from "@/types/folderTypes"; | ||
| import Image from "next/image"; | ||
| import useModalStore from "@/store/useModalStore"; | ||
| import useRerenderFolderList from "../../hooks/useRerenderFolderList"; | ||
|
|
||
| interface FolderActionsMenuProps { | ||
| setFolderList: React.Dispatch<React.SetStateAction<FolderData[]>>; | ||
| } | ||
|
|
||
| const FolderActionsMenu = ({ setFolderList }: FolderActionsMenuProps) => { | ||
| const { isOpen, openModal } = useModalStore(); | ||
|
|
||
| const handleModalOpen = (text: string) => { | ||
| switch (text) { | ||
| case "공유": | ||
| openModal("SNSModal"); | ||
| break; | ||
| case "이름 변경": | ||
| openModal("EditModal"); | ||
| break; | ||
| case "삭제": | ||
| openModal("DeleteFolderModal"); | ||
| break; | ||
| default: | ||
| break; | ||
| } | ||
| }; | ||
|
|
||
| useRerenderFolderList(isOpen, setFolderList); | ||
|
|
||
| return ( | ||
| <div className="w-[192px] h-[18px] flex justify-between gap-[12px] text-gray400"> | ||
| {[ | ||
| { src: "/icons/share.svg", alt: "공유", text: "공유" }, | ||
| { src: "/icons/pen.svg", alt: "이름 변경", text: "이름 변경" }, | ||
| { src: "/icons/delete.svg", alt: "삭제", text: "삭제" }, | ||
| ].map(({ src, alt, text }) => ( | ||
| <button | ||
| key={text} | ||
| className="flex items-center gap-[4px] text-sm" | ||
| onClick={() => handleModalOpen(text)} | ||
| > | ||
| <Image width={18} height={18} src={src} alt={alt} /> | ||
| <span>{text}</span> | ||
| </button> | ||
| ))} | ||
| </div> | ||
| ); | ||
| }; | ||
|
|
||
| export default FolderActionsMenu; | ||
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| interface SearchResultMessageProps { | ||
| message: string | string[]; | ||
| } | ||
|
|
||
| const SearchResultMessage = ({ message }: SearchResultMessageProps) => { | ||
| return ( | ||
| <div className="text-[32px] text-gray400 mt-[40px]"> | ||
| <span className="text-black300">"{message}"</span>으로 검색한 | ||
| 결과입니다. | ||
| </div> | ||
| ); | ||
| }; | ||
|
|
||
| export default SearchResultMessage; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| import { useEffect } from "react"; | ||
| import { proxy } from "@/lib/api/axiosInstanceApi"; | ||
| import { LinkData } from "@/types/linkTypes"; | ||
| import useViewport from "./useViewport"; | ||
|
|
||
| // 링크페이지의 query가 바뀌면 새로운 리스트로 업데이트 해주는 훅 | ||
| const useFetchLinks = ( | ||
| query: { | ||
| page?: number; | ||
| search?: string; | ||
| }, | ||
| setLinkCardList: (list: LinkData[]) => void | ||
| ) => { | ||
| const { isTablet } = useViewport(); | ||
|
|
||
| useEffect(() => { | ||
| const fetchLinks = async () => { | ||
| const res = await proxy.get("/api/links", { | ||
| params: { | ||
| page: query.page, | ||
| pageSize: isTablet ? 6 : 10, | ||
| search: query.search, | ||
| }, | ||
| }); | ||
| setLinkCardList(res.data.list); | ||
| }; | ||
|
|
||
| if (query) fetchLinks(); | ||
| }, [query, setLinkCardList, isTablet]); | ||
| }; | ||
|
|
||
| export default useFetchLinks; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| import { useEffect, useRef } from "react"; | ||
| import { getFolders } from "@/lib/api/folder"; | ||
| import { FolderData } from "@/types/folderTypes"; | ||
|
|
||
| // 모달이 닫혔을 때 새로운 FolderList를 보여주는 커스텀 훅 | ||
| const useRerenderFolderList = ( | ||
| isOpen: boolean, | ||
| setFolderList: React.Dispatch<React.SetStateAction<FolderData[]>> | ||
| ) => { | ||
| const isFirstRender = useRef(true); | ||
|
|
||
| useEffect(() => { | ||
| if (isFirstRender.current) { | ||
| isFirstRender.current = false; | ||
| return; // 최초 로드 시에 불필요한 fetch 요청을 막아줌. | ||
| } | ||
|
|
||
| const fetchNewFolderList = async () => { | ||
| const res = await getFolders(); | ||
| setFolderList(res); | ||
| }; | ||
|
|
||
| if (!isOpen) { | ||
| fetchNewFolderList(); // 드랍다운이 한번 열리고 닫혔을 때 데이터 fetch | ||
| } | ||
| }, [isOpen, setFolderList]); | ||
| }; | ||
|
|
||
| export default useRerenderFolderList; |
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 잘 사용하겠습니다 😭 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| import { useState, useEffect } from "react"; | ||
|
|
||
| const breakpoints = { | ||
| PC: { min: 1200 }, | ||
| Tablet: { min: 768, max: 1199 }, | ||
| Mobile: { min: 343, max: 767 }, | ||
| }; | ||
|
|
||
| // 현재 브라우저의 innerWidth와 반응형 상태를 반환하는 훅 | ||
| function useViewport(initialWidth = 0) { | ||
| const [width, setWidth] = useState(initialWidth); | ||
|
|
||
| const handleResize = () => { | ||
| setWidth(window.innerWidth); | ||
| }; | ||
|
|
||
| useEffect(() => { | ||
| handleResize(); | ||
| window.addEventListener("resize", handleResize); | ||
| return () => window.removeEventListener("resize", handleResize); | ||
| }, []); | ||
|
|
||
| const isPC = width >= breakpoints.PC.min; | ||
| const isTablet = | ||
| width >= breakpoints.Tablet.min && width <= breakpoints.Tablet.max; | ||
| const isMobile = | ||
| width >= breakpoints.Mobile.min && width <= breakpoints.Mobile.max; | ||
|
|
||
| return { width, isPC, isTablet, isMobile }; | ||
| } | ||
|
|
||
| export default useViewport; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| import { proxy } from "./axiosInstanceApi"; | ||
|
|
||
| // SSR에서 proxy로 요청 보낼 때 사용하는 로직 ㅜㅊ상화 | ||
|
||
| const fetchProxy = async (endpoint: string, req: any) => { | ||
| const headers = req ? { Cookie: req.headers.cookie } : undefined; | ||
| const response = await proxy.get(endpoint, { headers }); | ||
| return response.data; | ||
| }; | ||
|
|
||
| export default fetchProxy; | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
버튼 구현 이렇게 하니까 깔끔하네요