1+ "use client" ;
2+
3+ import { useEffect , useState , useRef } from "react" ;
14import { DashboardColumn , TaskCardList } from "@/lib/types" ;
25import { fetchTaskCardList } from "@/lib/apis/cardsApi" ;
36import { TOKEN_1 } from "@/lib/constants/tokens" ;
47import EditColumnButton from "./EditColumnButton" ;
58import AddTaskButton from "./AddTaskButton" ;
69import TaskCard from "./TaskCard" ;
710
8- export default async function Column ( { id, title } : DashboardColumn ) {
9- const { cards, totalCount, cursorId } = await fetchTaskCardList ( {
10- token : TOKEN_1 ,
11- id : id ,
12- } ) ;
13- const items : TaskCardList [ ] = cards ;
11+ const PAGE_SIZE = 3 ;
12+
13+ export default function Column ( { id, title } : DashboardColumn ) {
14+ const [ items , setItems ] = useState < TaskCardList [ ] > ( [ ] ) ;
15+ const [ cursorId , setCursorId ] = useState < number | null > ( null ) ;
16+ const [ totalCount , setTotalCount ] = useState ( 0 ) ;
17+ const [ isLoading , setIsLoading ] = useState ( false ) ;
18+ const [ isLast , setIsLast ] = useState ( false ) ;
19+ const observerRef = useRef < HTMLDivElement | null > ( null ) ;
20+
21+ const handleLoad = async ( ) => {
22+ if ( isLoading || isLast ) return ;
23+ setIsLoading ( true ) ;
24+
25+ try {
26+ const {
27+ cards : newCards ,
28+ cursorId : nextCursorId ,
29+ totalCount,
30+ } = await fetchTaskCardList ( {
31+ token : TOKEN_1 ,
32+ size : PAGE_SIZE ,
33+ cursorId,
34+ columnId : id ,
35+ } ) ;
36+
37+ setItems ( ( prev ) => [ ...prev , ...newCards ] ) ;
38+ setCursorId ( nextCursorId ) ;
39+ setTotalCount ( totalCount ) ;
40+
41+ if ( newCards . length < PAGE_SIZE || nextCursorId === null ) {
42+ setIsLast ( true ) ;
43+ }
44+ } finally {
45+ setIsLoading ( false ) ;
46+ }
47+ } ;
48+
49+ useEffect ( ( ) => {
50+ handleLoad ( ) ;
51+ } , [ ] ) ;
1452
15- // 해당 값 사용하게 되면(페이지네이션) 지울 테스트 코드
16- console . log ( cursorId ) ;
53+ useEffect ( ( ) => {
54+ if ( isLast ) return ;
55+
56+ const observer = new IntersectionObserver (
57+ ( entries ) => {
58+ if ( entries [ 0 ] . isIntersecting ) {
59+ handleLoad ( ) ;
60+ }
61+ } ,
62+ { threshold : 0.5 }
63+ ) ;
64+
65+ const current = observerRef . current ;
66+ if ( current ) observer . observe ( current ) ;
67+
68+ return ( ) => {
69+ if ( current ) observer . unobserve ( current ) ;
70+ observer . disconnect ( ) ;
71+ } ;
72+ } , [ cursorId , isLoading , isLast ] ) ;
1773
1874 return (
19- < 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" >
20- < div className = "flex flex-col gap-6 tablet:gap-[25px]" >
75+ < 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" >
76+ < div className = "flex flex-col gap-6 h-full tablet:gap-[25px]" >
2177 < div className = "flex justify-between" >
2278 < div className = "flex gap-3 items-center" >
2379 < div className = "flex gap-2 items-center" >
@@ -32,11 +88,20 @@ export default async function Column({ id, title }: DashboardColumn) {
3288 </ div >
3389 < EditColumnButton columnId = { id } columnTitle = { title } />
3490 </ div >
35- < div className = "flex flex-col gap-[10px] tablet:gap-4" >
36- < AddTaskButton />
37- { items . map ( ( item ) => (
38- < TaskCard key = { item . id } { ...item } columnTitle = { title } />
39- ) ) }
91+ < div className = "flex flex-col gap-[10px] flex-grow min-h-0 tablet:gap-4" >
92+ < div >
93+ < AddTaskButton />
94+ </ div >
95+ < div className = "flex flex-col gap-[10px] flex-grow min-h-0 overflow-y-auto whitespace-nowrap scrollbar-hide tablet:gap-4" >
96+ { items . map ( ( item , index ) => (
97+ < div
98+ key = { item . id }
99+ ref = { index === items . length - 1 ? observerRef : null }
100+ >
101+ < TaskCard { ...item } columnTitle = { title } />
102+ </ div >
103+ ) ) }
104+ </ div >
40105 </ div >
41106 </ div >
42107 </ div >
0 commit comments