Skip to content
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

feat: 전체 방 조회 API 연결 및 기능 구현 #164

Merged
merged 11 commits into from
Nov 18, 2023
43 changes: 34 additions & 9 deletions src/RoomSearch/components/ResultList.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,44 @@
import { Room } from '@/RoomList/mocks/types/rooms';
import { RoomSelectType } from '@/core/types';
import { useInfiniteSearch } from '@/core/api/queries';
import useIntersectionObserver from '../hooks/useIntersectionObserver';
import ResultListFallback from './ResultListFallback';
import { RoomAccordion } from '@/RoomList';
import { Deffered } from '@/shared/Deffered';

interface ResultListProps {
rooms: Room[];
type: RoomSelectType;
size: number;
}

const ResultList = ({ rooms }: ResultListProps) => {
const ResultList = ({ type, size }: ResultListProps) => {
const { fetchNextPage, results, isFetchingNextPage } = useInfiniteSearch({
type,
size
});
const [intersectionRef] = useIntersectionObserver({
threshold: 0.5,
onObserve: fetchNextPage
});

return (
<div className="flex flex-col gap-2">
{rooms.map((room) => (
<RoomAccordion
room={room}
key={room.id}
/>
))}
{results.map((rooms) =>
rooms.map((room) => (
<RoomAccordion
room={room}
key={room.id}
/>
))
)}
{isFetchingNextPage && (
<Deffered>
<ResultListFallback size={size} />
</Deffered>
)}
<div
ref={intersectionRef}
className="h-4"
></div>
</div>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

고생하셨습니다 추후에 hasNextPage 속성을 이용해서 방이 더 이상 없다는 문구도 표시해주면 더 좋을 것 같습니다 ㄷㄷ

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아하 그렇네유 ! 문구 추천 부탁드립니다 꾸벅 ( _ _ )
더 이상 방이 없네요! 이거 .. ?

);
};
Expand Down
20 changes: 20 additions & 0 deletions src/RoomSearch/components/ResultListFallback.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import React from 'react';

interface ResultListFallbackProps {
size?: number;
}

const ResultListFallback = ({ size = 10 }: ResultListFallbackProps) => {
return (
<div className="flex flex-col gap-2 opacity-60">
{Array.from({ length: size }, (_, index) => (
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오 size를 인수로 받아 개수를 자유롭게 설정할 수 있군요👍🏻👍🏻

<div
key={index}
className="my-1 h-24 w-full animate-pulse rounded-2xl bg-dark-gray"
></div>
))}
</div>
);
};

export default ResultListFallback;
46 changes: 46 additions & 0 deletions src/RoomSearch/hooks/useIntersectionObserver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { useCallback, useEffect, useRef, useState } from 'react';

interface ObserverOptions {
root?: Element | null;
rootMargin?: string;
threshold?: number;
onObserve: VoidFunction;
}

const useIntersectionObserver = ({
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오.. IntersectionObserver API를 쉽게 활용할 수 있게 hooks로 만들어주셨군요! 👍👍

onObserve,
...options
}: ObserverOptions) => {
const [isIntersecting, setIntersecting] = useState(false);
const intersectionRef = useRef<HTMLDivElement>(null);

const handleIntersect: IntersectionObserverCallback = useCallback(
(entries, observer) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
setIntersecting(true);
onObserve();
observer.unobserve(entry.target);
} else {
setIntersecting(false);
}
});
},
[onObserve]
);

useEffect(() => {
const observer = new IntersectionObserver(handleIntersect, options);
const intersectionElement = intersectionRef.current;

intersectionElement && observer.observe(intersectionElement);

return () => {
intersectionElement && observer.unobserve(intersectionElement);
};
}, [handleIntersect, options]);

return [intersectionRef, isIntersecting] as const;
};

export default useIntersectionObserver;
2 changes: 1 addition & 1 deletion src/core/api/functions/couponAPI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { CouponStatus } from '@/core/types';
import { baseInstance } from '../instance';

const couponAPI = {
postAllCoupons: async (body: CouponStatus) => {
postCouponsByStatus: async (body: CouponStatus) => {
return await baseInstance.post<Coupons>('/coupons/search', body);
}
};
Expand Down
6 changes: 6 additions & 0 deletions src/core/api/functions/roomAPI.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { RoomsRequestParams, TotalRooms } from '@/core/types';
import { baseInstance } from '../instance';
import { MyJoinRoom } from '@/core/types/MyJoinRoom';
import { RoomInfo } from '@/core/types/Room';
Expand Down Expand Up @@ -49,6 +50,11 @@ const roomAPI = {
return await baseInstance.put(
`/rooms/${roomId}/members/${memberId}/delegation`
);
},

getRoomsAll: async (params?: RoomsRequestParams) => {
const response: TotalRooms = await baseInstance.get('/rooms', { params });
return response.rooms;
}
};

Expand Down
2 changes: 1 addition & 1 deletion src/core/api/options/coupon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const couponOptions = {
all: (body: CouponStatus) =>
queryOptions({
queryKey: ['coupons', body] as const,
queryFn: () => couponAPI.postAllCoupons(body)
queryFn: () => couponAPI.postCouponsByStatus(body)
})
};

Expand Down
7 changes: 7 additions & 0 deletions src/core/api/options/room.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { queryOptions } from '@tanstack/react-query';
import { RoomsRequestParams } from '@/core/types';
import roomAPI from '../functions/roomAPI';

const roomOptions = {
Expand All @@ -12,6 +13,12 @@ const roomOptions = {
queryOptions({
queryKey: ['rooms', 'myJoin'] as const,
queryFn: () => roomAPI.getMyJoinRoom()
}),

all: (params?: RoomsRequestParams) =>
queryOptions({
queryKey: ['rooms', params?.type || 'all'] as const,
queryFn: () => roomAPI.getRoomsAll(params)
})
};

Expand Down
1 change: 1 addition & 0 deletions src/core/api/queries/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as useInfiniteSearch } from './useInfiniteSearch';
31 changes: 31 additions & 0 deletions src/core/api/queries/useInfiniteSearch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { useSuspenseInfiniteQuery } from '@tanstack/react-query';
import { RoomSelectType } from '@/core/types';
import roomAPI from '../functions/roomAPI';

const useInfiniteSearch = ({
type,
size
}: {
type: RoomSelectType;
size: number;
}) => {
const { fetchNextPage, hasNextPage, data, isFetchingNextPage } =
useSuspenseInfiniteQuery({
queryKey: ['rooms', type],
queryFn: ({ pageParam }) =>
roomAPI.getRoomsAll({ page: pageParam, type, size }),
initialPageParam: 1,
getNextPageParam: (lastPage, allPages, lastPageParam) =>
lastPage.length < size ? null : lastPageParam + 1,
select: ({ pages }) => pages
});

return {
fetchNextPage,
hasNextPage,
results: data,
isFetchingNextPage
};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3:
UseSuspenseInfiniteQueryResult 타입의 데이터 중 일부만 추출하고 export 해주셨군요!! 😮
현재 export 되는 모든 데이터는 useSuspenseInfiniteQuery 를 호출한 결과만 바로 반환해도 가져올 수 있을 것 같은데, 특정 프로퍼티만 구조 분해 할당 한 뒤에 반환하셨던 의도가 있으셨는지 궁금해요!!

data 프로퍼티는 results 라는 프로퍼티명으로 변경한 뒤에 반환되고 있는데, TanStack Query의 useInfiniteQuery 에는 해당 프로퍼티가 없다보니 TanStack Query는 알고 있지만 이 커스텀 훅은 아직 모르는 개발자가 이 훅을 사용하는 곳의 코드를 봤을 때, results 라는 프로퍼티를 알기 위해서 다시 한번 훅의 인터페이스를 살펴봐야 하는 과정이 생길 수도 있을 것 같다는 매우매우 개인적인 의견입니다!!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

크게 생각하고 작성한 내용은 아닙니다만 허허 ,,!
커스텀 훅을 작성할 때 사용하는 값들만 리턴하는 습관이 있다 보니 (약간 가독성을 위한 ?) 그랬던 것 같아요!
이 페이지에서만 쓰는 훅이기 때문에, 쓰는 프로퍼티가 정해져 있어서 확장해서 쓸 생각은 안한 것도 있습니닷

그치만 어차피 컴포넌트 내에서 필요한 친구들만 빼서 쓰기 때문에 무관할 것 같네용
게다가 수정 횟수도 줄어들고.. ! 반영했습니다 굳.

data 도 사실 data.map => rooms.map 을 하는게 보기 어려울 것 같아서 네이밍을 바꿔치기 한 것이었는데,
상훈님이 말씀하신 걸 보니 라이브러리 표준 네이밍을 지키는 편이 훨씬 좋겠네요 !! 모두 반영합니닷 👍

};

export default useInfiniteSearch;
Loading