diff --git a/src/app/match/[id]/page.tsx b/src/app/match/[id]/page.tsx index 1b754a4..27375d0 100644 --- a/src/app/match/[id]/page.tsx +++ b/src/app/match/[id]/page.tsx @@ -98,8 +98,8 @@ export default function Match({ params }: { params: { id: string } }) { scrollToBottom={scrollToBottom} {...data} /> - <CommentList commentList={comments} /> - <div ref={scrollRef}></div> + <CommentList.SocketList commentList={comments} /> + <li ref={scrollRef}></li> </ul> <CommentForm matchId={params.id} diff --git a/src/components/match/CommentList/index.tsx b/src/components/match/CommentList/index.tsx index d03934b..1628aef 100644 --- a/src/components/match/CommentList/index.tsx +++ b/src/components/match/CommentList/index.tsx @@ -2,22 +2,35 @@ import { useEffect } from 'react'; +import useInfiniteObserver from '@/hooks/useInfiniteObserver'; import { MatchCommentType } from '@/types/match'; import CommentItem from '../CommentItem'; type CommentListProps = { commentList: MatchCommentType[]; - hasNextPage?: boolean; - fetchNextPage?: () => void; - isFetching?: boolean; - scrollToBottom?: () => void; + hasNextPage: boolean; + fetchNextPage: () => void; + isFetching: boolean; + scrollToBottom: () => void; }; export default function CommentList({ commentList, + fetchNextPage, + hasNextPage, + isFetching, scrollToBottom, }: CommentListProps) { + const { ref } = useInfiniteObserver<HTMLDivElement>( + async (entry, observer) => { + observer.unobserve(entry.target); + if (hasNextPage && !isFetching) { + fetchNextPage(); + } + }, + ); + useEffect(() => { if (!scrollToBottom) return; @@ -26,9 +39,22 @@ export default function CommentList({ return ( <> + <div ref={ref}></div> {commentList.map(comment => ( <CommentItem {...comment} key={comment.commentId} order={1} /> ))} </> ); } + +CommentList.SocketList = function SocketList({ + commentList, +}: Pick<CommentListProps, 'commentList'>) { + return ( + <> + {commentList.map(comment => ( + <CommentItem {...comment} key={comment.commentId} order={1} /> + ))} + </> + ); +}; diff --git a/src/hooks/useInfiniteObserver.ts b/src/hooks/useInfiniteObserver.ts new file mode 100644 index 0000000..3283634 --- /dev/null +++ b/src/hooks/useInfiniteObserver.ts @@ -0,0 +1,33 @@ +import { useCallback, useEffect, useRef } from 'react'; + +type IntersectHandler = ( + entry: IntersectionObserverEntry, + observer: IntersectionObserver, +) => void; + +export default function useIntersect<T extends HTMLElement>( + onIntersect: IntersectHandler, + options?: IntersectionObserverInit, +) { + const ref = useRef<T | null>(null); + const callback = useCallback( + (entries: IntersectionObserverEntry[], observer: IntersectionObserver) => { + entries.forEach(entry => { + if (entry.isIntersecting) onIntersect(entry, observer); + }); + }, + [onIntersect], + ); + + useEffect(() => { + if (!ref.current) return; + + const observer = new IntersectionObserver(callback, options); + + observer.observe(ref.current); + + return () => observer.disconnect(); + }, [ref, options, callback]); + + return { ref }; +}