feat: 공지사항 무한스크롤 추가

This commit is contained in:
jhynsoo 2024-08-11 22:55:11 +09:00
parent 071d7d6e51
commit f20bd2dd51
6 changed files with 53 additions and 9 deletions

View File

@ -0,0 +1,33 @@
import { useEffect, useRef } from 'react';
export default function IntersectionArea({ onObserve, once = false }) {
const ref = useRef(null);
useEffect(() => {
const callback = (entries, observer) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
onObserve();
if (once) {
observer.disconnect();
}
}
});
};
const observer = new IntersectionObserver(callback, {
root: null,
rootMargin: '0px',
threshold: 0.5,
});
if (ref.current) {
observer.observe(ref.current);
}
return () => {
observer.disconnect();
};
}, [onObserve, once]);
return <div ref={ref} />;
}

View File

@ -0,0 +1 @@
export { default as IntersectionArea } from './IntersectionArea';

View File

@ -2,3 +2,4 @@ export const API_URL = import.meta.env.VITE_API_URL;
export const ROOM_URL = import.meta.env.VITE_ROOM_URL; export const ROOM_URL = import.meta.env.VITE_ROOM_URL;
export const CHAT_URL = import.meta.env.VITE_CHAT_URL; export const CHAT_URL = import.meta.env.VITE_CHAT_URL;
export const STATIC_URL = import.meta.env.VITE_STATIC_URL; export const STATIC_URL = import.meta.env.VITE_STATIC_URL;
export const PAGE_SIZE = 20;

View File

@ -1,10 +1,17 @@
import { useSuspenseQuery } from '@tanstack/react-query'; import { useSuspenseInfiniteQuery } from '@tanstack/react-query';
import instance from '../../utils/axios/instance'; import instance from '../../utils/axios/instance';
import { API_URL } from '../../constants'; import { API_URL, PAGE_SIZE } from '../../constants';
export function useNotices(lectureId, page = 0) { export function useNotices(lectureId) {
return useSuspenseQuery({ return useSuspenseInfiniteQuery({
queryKey: ['noticelist', lectureId, page], queryKey: ['noticelist', lectureId],
queryFn: () => instance.get(`${API_URL}/board?lectureId=${lectureId}&category=announcement&pageNo=${page}`), queryFn: ({ pageParam = 0 }) =>
instance.get(`${API_URL}/board?lectureId=${lectureId}&category=announcement&pageNo=${pageParam}`),
getNextPageParam: (lastPage, allPages) => {
if (lastPage.data.length < PAGE_SIZE) {
return undefined;
}
return allPages.length + 1;
},
}); });
} }

View File

@ -7,7 +7,7 @@ import { useQnas } from '../../hooks/api/useQnas';
export default function LearningLectureDetailPage() { export default function LearningLectureDetailPage() {
const { lectureId } = useParams(); const { lectureId } = useParams();
const { data: noticesData } = useNotices(lectureId); const { data: noticesData } = useNotices(lectureId);
const notices = noticesData?.data.slice(0, 3); const notices = noticesData.pages[0]?.data.slice(0, 3);
const { data: qnasData } = useQnas(lectureId); const { data: qnasData } = useQnas(lectureId);
const questions = qnasData?.data.slice(0, 3); const questions = qnasData?.data.slice(0, 3);

View File

@ -3,11 +3,12 @@ import ArticleBoard from '../../components/ArticleBoard/ArticleBoard';
import { useNotices } from '../../hooks/api/useNotices'; import { useNotices } from '../../hooks/api/useNotices';
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import useBoundStore from '../../store'; import useBoundStore from '../../store';
import IntersectionArea from '../../components/IntersectionArea/IntersectionObserver';
export default function NoticeListPage() { export default function NoticeListPage() {
const { lectureId } = useParams(); const { lectureId } = useParams();
const { data } = useNotices(lectureId); const { data, fetchNextPage, hasNextPage } = useNotices(lectureId);
const notices = data?.data; const notices = data?.pages.flatMap((page) => page.data);
const userType = useBoundStore((state) => state.userType); const userType = useBoundStore((state) => state.userType);
return ( return (
@ -24,6 +25,7 @@ export default function NoticeListPage() {
to={`${notice.id}`} to={`${notice.id}`}
/> />
))} ))}
{hasNextPage && <IntersectionArea onObserve={() => fetchNextPage()} />}
</ArticleBoard> </ArticleBoard>
); );
} }