Skip to content

Commit c1c562c

Browse files
committed
fix: 편지 상세 무한 스크롤 오류 수정
- 마지막 편지 보일 시 새로 페이징 하는 로직을 response data에 맞게 수정 - react-intersection-observer 라이브러리를 이용하여 마지막 요소가 view에 들어오는지 확인
1 parent 6d821ae commit c1c562c

File tree

6 files changed

+90
-79
lines changed

6 files changed

+90
-79
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
"gsap": "^3.12.7",
2222
"react": "^18.3.1",
2323
"react-dom": "^18.3.1",
24+
"react-intersection-observer": "^9.15.1",
2425
"react-router": "^7.1.5",
2526
"swiper": "^11.2.4",
2627
"tailwind-merge": "^3.0.1",

pnpm-lock.yaml

Lines changed: 18 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/apis/mailBox.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ export const getMailbox = async () => {
1010
}
1111
};
1212

13-
export const getMailboxDetail = async (id: number) => {
13+
export const getMailboxDetail = async (id: number, pageParam: number) => {
1414
try {
15-
// const response = await client.get(`/api/mailbox/${id}/page=${pageParam}&size=20`);
16-
const response = await client.get(`/api/mailbox/${id}`);
15+
const response = await client.get(`/api/mailbox/${id}?page=${pageParam}&size=20`);
16+
// const response = await client.get(`/api/mailbox/${id}`);
1717

1818
if (!response) throw new Error('error while fetching mailbox detail data');
1919
return response.data;

src/components/LetterWrapper.tsx

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { forwardRef } from 'react';
12
import { twMerge } from 'tailwind-merge';
23

34
interface LetterWrapperProps {
@@ -7,20 +8,23 @@ interface LetterWrapperProps {
78
onClick?: (e: React.MouseEvent<HTMLElement>) => void;
89
}
910

10-
const LetterWrapper = ({ isSender = false, className, children, onClick }: LetterWrapperProps) => {
11-
return (
12-
<article
13-
className={twMerge(
14-
'relative flex overflow-hidden rounded-sm p-4',
15-
isSender ? 'letter-sender-bg' : 'letter-receiver-bg',
16-
className,
17-
)}
18-
onClick={onClick}
19-
>
20-
<div className="z-10 w-full">{children}</div>
21-
<div className="absolute inset-0 z-0 bg-white/50 blur-xl" />
22-
</article>
23-
);
24-
};
11+
const LetterWrapper = forwardRef<HTMLDivElement, LetterWrapperProps>(
12+
({ isSender = false, className, children, onClick }, ref) => {
13+
return (
14+
<article
15+
className={twMerge(
16+
'relative flex overflow-hidden rounded-sm p-4',
17+
isSender ? 'letter-sender-bg' : 'letter-receiver-bg',
18+
className,
19+
)}
20+
onClick={onClick}
21+
ref={ref}
22+
>
23+
<div className="z-10 w-full">{children}</div>
24+
<div className="absolute inset-0 z-0 bg-white/50 blur-xl" />
25+
</article>
26+
);
27+
},
28+
);
2529

2630
export default LetterWrapper;

src/pages/LetterBoxDetail/components/LetterPreview.tsx

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { forwardRef } from 'react';
12
import { useNavigate } from 'react-router';
23

34
import LetterWrapper from '@/components/LetterWrapper';
@@ -14,18 +15,18 @@ interface LetterPreviewProps {
1415
isClosed: boolean;
1516
zipCode: string;
1617
}
17-
const LetterPreview = ({
18-
id,
19-
date,
20-
title,
21-
isSend,
22-
checked,
23-
isShareMode = false,
24-
onToggle,
25-
isClosed,
26-
zipCode,
27-
}: LetterPreviewProps) => {
28-
// 차단된 편지인경우 편지 보내기 disable
18+
const LetterPreview = forwardRef<HTMLDivElement, LetterPreviewProps>((props, ref) => {
19+
const {
20+
id,
21+
date,
22+
title,
23+
isSend,
24+
checked,
25+
isShareMode = false,
26+
onToggle,
27+
isClosed,
28+
zipCode,
29+
} = props;
2930
const navigate = useNavigate();
3031

3132
const handleItemClick = (id: number) => {
@@ -40,7 +41,7 @@ const LetterPreview = ({
4041

4142
if (isShareMode)
4243
return (
43-
<LetterWrapper isSender={isSend}>
44+
<LetterWrapper isSender={isSend} ref={ref}>
4445
<div className="mb-3 flex items-center justify-between">
4546
<p className="body-r text-gray-80">{formatDate(date)}</p>
4647
<label htmlFor={`${id}`} className="relative">
@@ -59,11 +60,11 @@ const LetterPreview = ({
5960
);
6061

6162
return (
62-
<LetterWrapper isSender={isSend} onClick={() => handleItemClick(id)}>
63+
<LetterWrapper isSender={isSend} onClick={() => handleItemClick(id)} ref={ref}>
6364
<p className="body-r text-gray-80 mb-3">{date}</p>
6465
<p className="body-m text-gray-80 line-clamp-1 break-all">{title}</p>
6566
</LetterWrapper>
6667
);
67-
};
68+
});
6869

6970
export default LetterPreview;

src/pages/LetterBoxDetail/index.tsx

Lines changed: 33 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import { useMutation, useQuery } from '@tanstack/react-query';
2-
import { ChangeEvent, useState } from 'react';
1+
import { useMutation, useInfiniteQuery } from '@tanstack/react-query';
2+
import { ChangeEvent, useEffect, useState } from 'react';
3+
import { useInView } from 'react-intersection-observer';
34
import { useLocation, useNavigate } from 'react-router';
45

56
import { getMailboxDetail, postMailboxDisconnect } from '@/apis/mailBox';
@@ -10,8 +11,6 @@ import PageTitle from '@/components/PageTitle';
1011

1112
import InformationTooltip from './components/InformationTooltip';
1213
import LetterPreview from './components/LetterPreview';
13-
14-
// import useAuthStore from '@/stores/authStore';
1514
interface MailBoxDetailProps {
1615
letterId: number;
1716
title: string;
@@ -23,7 +22,6 @@ interface MailBoxDetailProps {
2322
const LetterBoxDetailPage = () => {
2423
const location = useLocation();
2524
const userInfo = { ...location.state };
26-
// const {userId} = useAuthStore.getState();
2725

2826
const [isShareMode, setShareMode] = useState(false);
2927
const [isOpenDisConnectModal, setIsOpenDisConnectModal] = useState(false);
@@ -33,45 +31,33 @@ const LetterBoxDetailPage = () => {
3331

3432
const navigate = useNavigate();
3533

36-
// const {
37-
// data: mailLists = [],
38-
// isLoading,
39-
// isError,
40-
// fetchNextPage,
41-
// hasNextPage,
42-
// isFetchingNextPage,
43-
// } = useInfiniteQuery({
44-
// queryKey: ['mailBoxDetail', userInfo.id],
45-
// queryFn: async ({ pageParam }) => {
46-
// const response = await getMailboxDetail(userInfo.id, pageParam);
47-
// console.log(response.data);
48-
// return response.data as MailBoxDetailProps[];
49-
// },
50-
// enabled: !!userInfo.id,
51-
// initialPageParam: 0,
52-
// getNextPageParam: (lastPage, allPages) => {
53-
// return lastPage?.length == 0 || !lastPage || lastPage?.length < 20
54-
// ? undefined
55-
// : allPages.length + 1;
56-
// },
57-
// staleTime: 1000 * 60 * 5,
58-
// gcTime: 1000 * 60 * 10,
59-
// });
60-
61-
const {
62-
data: mailLists = [],
63-
isLoading,
64-
isError,
65-
} = useQuery({
66-
queryKey: ['mailBoxDetail', userInfo.id],
67-
queryFn: async () => {
68-
const response = await getMailboxDetail(userInfo.id);
69-
console.log(response.data);
70-
return response.data as MailBoxDetailProps[];
71-
},
72-
staleTime: 1000 * 60 * 5,
73-
gcTime: 1000 * 60 * 10,
74-
});
34+
const { data, isLoading, isError, fetchNextPage, hasNextPage, isFetchingNextPage } =
35+
useInfiniteQuery({
36+
queryKey: ['mailBoxDetail', userInfo.id],
37+
queryFn: async ({ pageParam }) => {
38+
console.log(`Fetching page: ${pageParam}`); // 디버깅용
39+
const response = await getMailboxDetail(userInfo.id, pageParam);
40+
console.log(response.data);
41+
return response.data;
42+
},
43+
enabled: !!userInfo.id,
44+
initialPageParam: 0,
45+
getNextPageParam: (lastPage, allPages) => {
46+
return lastPage.currentPage >= lastPage.totalPages ? undefined : allPages.length + 1;
47+
},
48+
staleTime: 1000 * 60 * 5,
49+
gcTime: 1000 * 60 * 10,
50+
});
51+
52+
const mailLists: MailBoxDetailProps[] = data?.pages.flatMap((page) => page.content) || [];
53+
54+
const { ref, inView } = useInView();
55+
56+
useEffect(() => {
57+
if (inView && hasNextPage && !isFetchingNextPage) {
58+
fetchNextPage();
59+
}
60+
}, [inView, hasNextPage, isFetchingNextPage, fetchNextPage]);
7561

7662
const disconnectMutation = useMutation({
7763
mutationFn: async () => await postMailboxDisconnect(userInfo.id),
@@ -119,7 +105,7 @@ const LetterBoxDetailPage = () => {
119105
};
120106

121107
if (isError) {
122-
navigate('/NotFound');
108+
navigate('/notFound');
123109
}
124110

125111
return (
@@ -179,7 +165,7 @@ const LetterBoxDetailPage = () => {
179165
//TODO: skeleton
180166
<div>Loading</div>
181167
) : (
182-
mailLists.map((letter) => (
168+
mailLists.map((letter, index) => (
183169
<LetterPreview
184170
key={letter.letterId}
185171
id={letter.letterId}
@@ -191,6 +177,7 @@ const LetterBoxDetailPage = () => {
191177
isClosed={userInfo.isClosed}
192178
onToggle={() => toggleSelected(letter.letterId)}
193179
zipCode={userInfo.zipCode}
180+
ref={index === mailLists.length - 1 ? ref : null}
194181
/>
195182
))
196183
)}

0 commit comments

Comments
 (0)