-
Notifications
You must be signed in to change notification settings - Fork 2
feat: #88/대시보드 상세 페이지 할 일 카드 무한 스크롤 #101
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
Changes from all commits
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 |
|---|---|---|
| @@ -1,23 +1,79 @@ | ||
| "use client"; | ||
|
|
||
| import { useEffect, useState, useRef } from "react"; | ||
| import { DashboardColumn, TaskCardList } from "@/lib/types"; | ||
| import { fetchTaskCardList } from "@/lib/apis/cardsApi"; | ||
| import { TOKEN_1 } from "@/lib/constants/tokens"; | ||
| import EditColumnButton from "./EditColumnButton"; | ||
| import AddTaskButton from "./AddTaskButton"; | ||
| import TaskCard from "./TaskCard"; | ||
|
|
||
| export default async function Column({ id, title }: DashboardColumn) { | ||
| const { cards, totalCount, cursorId } = await fetchTaskCardList({ | ||
| token: TOKEN_1, | ||
| id: id, | ||
| }); | ||
| const items: TaskCardList[] = cards; | ||
| const PAGE_SIZE = 3; | ||
|
|
||
| export default function Column({ id, title }: DashboardColumn) { | ||
| const [items, setItems] = useState<TaskCardList[]>([]); | ||
| const [cursorId, setCursorId] = useState<number | null>(null); | ||
| const [totalCount, setTotalCount] = useState(0); | ||
| const [isLoading, setIsLoading] = useState(false); | ||
| const [isLast, setIsLast] = useState(false); | ||
| const observerRef = useRef<HTMLDivElement | null>(null); | ||
|
|
||
| const handleLoad = async () => { | ||
| if (isLoading || isLast) return; | ||
| setIsLoading(true); | ||
|
|
||
| try { | ||
| const { | ||
| cards: newCards, | ||
| cursorId: nextCursorId, | ||
| totalCount, | ||
| } = await fetchTaskCardList({ | ||
| token: TOKEN_1, | ||
| size: PAGE_SIZE, | ||
| cursorId, | ||
| columnId: id, | ||
| }); | ||
|
|
||
| setItems((prev) => [...prev, ...newCards]); | ||
| setCursorId(nextCursorId); | ||
| setTotalCount(totalCount); | ||
|
|
||
| if (newCards.length < PAGE_SIZE || nextCursorId === null) { | ||
| setIsLast(true); | ||
| } | ||
| } finally { | ||
| setIsLoading(false); | ||
| } | ||
| }; | ||
|
|
||
| useEffect(() => { | ||
| handleLoad(); | ||
| }, []); | ||
|
|
||
| // 해당 값 사용하게 되면(페이지네이션) 지울 테스트 코드 | ||
| console.log(cursorId); | ||
| useEffect(() => { | ||
| if (isLast) return; | ||
|
|
||
| const observer = new IntersectionObserver( | ||
| (entries) => { | ||
| if (entries[0].isIntersecting) { | ||
| handleLoad(); | ||
| } | ||
| }, | ||
| { threshold: 0.5 } | ||
| ); | ||
|
|
||
| const current = observerRef.current; | ||
| if (current) observer.observe(current); | ||
|
|
||
| return () => { | ||
| if (current) observer.unobserve(current); | ||
| observer.disconnect(); | ||
| }; | ||
| }, [cursorId, isLoading, isLast]); | ||
|
Comment on lines
+53
to
+72
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. observer하는거는 재사용의 목적 + 이 페이지에서는 비즈니스로직일 필요가 없어보여서 추상화해보면 좋겠네요.
Owner
Author
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. 다른 곳에서도 무한 스크롤을 사용해서 추상화해보면 좋겠네요. 의견 감사합니다! |
||
|
|
||
| return ( | ||
| <div className="py-4 border-b border-gray-300 tablet:px-5 tablet:pt-[22px] tablet:pb-5 pc:border-b-0 pc:border-r"> | ||
| <div className="flex flex-col gap-6 tablet:gap-[25px]"> | ||
| <div className="h-full py-4 border-b border-gray-300 tablet:px-5 tablet:pt-[22px] tablet:pb-5 pc:border-b-0 pc:border-r"> | ||
| <div className="flex flex-col gap-6 h-full tablet:gap-[25px]"> | ||
| <div className="flex justify-between"> | ||
| <div className="flex gap-3 items-center"> | ||
| <div className="flex gap-2 items-center"> | ||
|
|
@@ -32,11 +88,20 @@ export default async function Column({ id, title }: DashboardColumn) { | |
| </div> | ||
| <EditColumnButton columnId={id} columnTitle={title} /> | ||
| </div> | ||
| <div className="flex flex-col gap-[10px] tablet:gap-4"> | ||
| <AddTaskButton /> | ||
| {items.map((item) => ( | ||
| <TaskCard key={item.id} {...item} columnTitle={title} /> | ||
| ))} | ||
| <div className="flex flex-col gap-[10px] flex-grow min-h-0 tablet:gap-4"> | ||
| <div> | ||
| <AddTaskButton /> | ||
| </div> | ||
| <div className="flex flex-col gap-[10px] flex-grow min-h-0 overflow-y-auto whitespace-nowrap scrollbar-hide tablet:gap-4"> | ||
| {items.map((item, index) => ( | ||
| <div | ||
| key={item.id} | ||
| ref={index === items.length - 1 ? observerRef : null} | ||
| > | ||
| <TaskCard {...item} columnTitle={title} /> | ||
| </div> | ||
| ))} | ||
| </div> | ||
| </div> | ||
| </div> | ||
| </div> | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,12 +2,21 @@ import { BASE_URL } from "@/lib/constants/urls"; | |
|
|
||
| export async function fetchTaskCardList({ | ||
| token, | ||
| id, | ||
| size, | ||
| cursorId, | ||
| columnId, | ||
| }: { | ||
| token: string; | ||
| id: number; | ||
| size: number; | ||
| cursorId: number | null; | ||
| columnId: number; | ||
| }) { | ||
| const res = await fetch(`${BASE_URL}/cards?size=10&columnId=${id}`, { | ||
| let query = `size=${size}&columnId=${columnId}`; | ||
| if (cursorId !== null) { | ||
| query += `&cursorId=${cursorId}`; | ||
| } | ||
|
Comment on lines
+14
to
+17
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. 여기도 다른분들 생산성을 위해서 query로 붙여주는 함수로 한번 분리해보는것도 좋겠네요.
Owner
Author
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. 감사합니다 고려해보고 해보도록 하겠습니다! |
||
|
|
||
| const res = await fetch(`${BASE_URL}/cards?${query}`, { | ||
| headers: { | ||
| Accept: "application/json", | ||
| Authorization: `Bearer ${token}`, | ||
|
|
||
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.
로컬에서 작업하시면서 strict껐으면 git으로는 추적 안하는게 좋을것 같아요
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.
안 그래도 이 부분 지우는 걸 잊고 있었는데, 우선 다음 작업까지 하고 다시 true로 해놓겠습니다!