Skip to content

Commit 1d0e476

Browse files
authored
Refactor/칵테일 정렬 기능 수정 (#144)
* [refactor] 무한스크롤 tanstack * [refactor] 레시피페이지 리팩토링 * [refactor] 아코디언박스 * [feat]필터링 뒤로가기 스크롤 저장 * [chore] 머지 전 커밋 * [chore]머지 전 커밋 누락 내용 커밋 * [refactor]리팩토링 커밋 * [refactor] 정렬 중복아이템문제 * [feat] 칵테일 정렬기능 * [fix]댓글 알림 수정 * [chore] 충돌사항 수정
1 parent 3f0e6f3 commit 1d0e476

File tree

5 files changed

+81
-25
lines changed

5 files changed

+81
-25
lines changed

src/domains/recipe/api/fetchRecipe.ts

Lines changed: 59 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { getApi } from '@/app/api/config/appConfig';
22
import { useAuthStore } from '@/domains/shared/store/auth';
3-
import { useInfiniteQuery, useQuery } from '@tanstack/react-query';
3+
import { useInfiniteQuery, useQuery, useQueryClient } from '@tanstack/react-query';
44
import { Cocktail, Sort } from '../types/types';
5+
import { useEffect, useRef } from 'react';
56

67
interface CocktailResponse {
78
data: Cocktail[];
@@ -22,6 +23,11 @@ interface CocktailFilter extends SearchFilters {
2223
sortBy?: Sort;
2324
}
2425

26+
interface PageParam {
27+
lastId: number;
28+
lastValue: number | string;
29+
}
30+
2531
const fetchKeep = async (): Promise<Set<number>> => {
2632
const res = await fetch(`${getApi}/me/bar`, {
2733
method: 'GET',
@@ -36,15 +42,15 @@ const fetchKeep = async (): Promise<Set<number>> => {
3642
};
3743

3844
const fetchRecipe = async (
39-
lastId: number | null,
45+
pageParam: PageParam | null,
4046
size: number,
4147
sortBy?: Sort
4248
): Promise<Cocktail[]> => {
4349
const url = new URL(`${getApi}/cocktails`);
4450
url.searchParams.set('size', String(size));
45-
if (lastId !== null) {
46-
url.searchParams.set('lastId', String(lastId));
47-
url.searchParams.set('lastValue', String(lastId));
51+
if (pageParam) {
52+
url.searchParams.set('lastId', String(pageParam.lastId));
53+
url.searchParams.set('lastValue', String(pageParam.lastValue));
4854
}
4955

5056
if (sortBy) {
@@ -95,6 +101,18 @@ const hasActiveFilters = (filters: SearchFilters): boolean => {
95101

96102
export const useCocktailsInfiniteQuery = (size: number = 20, sortBy?: Sort) => {
97103
const user = useAuthStore((state) => state.user);
104+
const queryClient = useQueryClient();
105+
const prevSortBy = useRef(sortBy);
106+
107+
useEffect(() => {
108+
if (prevSortBy.current !== undefined && prevSortBy.current !== sortBy) {
109+
queryClient.removeQueries({
110+
queryKey: ['cocktails', 'infinite', prevSortBy.current],
111+
});
112+
}
113+
prevSortBy.current = sortBy;
114+
}, [sortBy, queryClient]);
115+
98116
return useInfiniteQuery({
99117
queryKey: ['cocktails', 'infinite', sortBy, size, user?.id],
100118
queryFn: async ({ pageParam }) => {
@@ -110,11 +128,37 @@ export const useCocktailsInfiniteQuery = (size: number = 20, sortBy?: Sort) => {
110128

111129
return cocktails;
112130
},
113-
getNextPageParam: (lastpage) => {
114-
if (lastpage.length < size) return undefined;
115-
return lastpage[lastpage.length - 1]?.cocktailId ?? undefined;
131+
getNextPageParam: (lastPage) => {
132+
if (lastPage.length < size) {
133+
return undefined;
134+
}
135+
136+
const lastItem = lastPage[lastPage.length - 1];
137+
if (!lastItem) return undefined;
138+
139+
let lastValue: number | string;
140+
141+
switch (sortBy) {
142+
case 'keeps':
143+
lastValue = lastItem.keepCount ?? lastItem.cocktailId;
144+
break;
145+
case 'comments':
146+
lastValue = lastItem.commentCount ?? lastItem.cocktailId;
147+
break;
148+
case 'recent':
149+
default:
150+
lastValue = lastItem.cocktailId;
151+
break;
152+
}
153+
154+
return {
155+
lastId: lastItem.cocktailId,
156+
lastValue: lastValue,
157+
};
116158
},
117-
initialPageParam: null as number | null,
159+
initialPageParam: null as PageParam | null,
160+
refetchOnMount: false,
161+
refetchOnWindowFocus: false,
118162
});
119163
};
120164

@@ -161,13 +205,17 @@ export const useCocktails = (
161205
}
162206

163207
const allCocktails = infiniteQuery.data?.pages.flatMap((page) => page) ?? [];
208+
const uniqueCocktails = allCocktails.filter(
209+
(cocktail, index, self) => index === self.findIndex((c) => c.cocktailId === cocktail.cocktailId)
210+
);
164211

212+
const hasDuplicates = allCocktails.length !== uniqueCocktails.length;
165213
return {
166-
data: allCocktails,
214+
data: uniqueCocktails,
167215
noResults: false,
168216
isSearchMode: false,
169217
fetchNextPage: infiniteQuery.fetchNextPage,
170-
hasNextPage: infiniteQuery.hasNextPage,
218+
hasNextPage: hasDuplicates ? false : infiniteQuery.hasNextPage,
171219
isFetchingNextPage: infiniteQuery.isFetchingNextPage,
172220
};
173221
};

src/domains/recipe/api/useRecipeComment.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ import { useAuthStore } from '@/domains/shared/store/auth';
44
import { useToast } from '@/shared/hook/useToast';
55
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
66

7+
interface Comment {
8+
userNickName: string;
9+
}
10+
711
export const postRecipeComment = async (cocktailId: number, content: string) => {
812
const body = {
913
cocktailId,
@@ -18,6 +22,8 @@ export const postRecipeComment = async (cocktailId: number, content: string) =>
1822
body: JSON.stringify(body),
1923
});
2024

25+
if (res.status === 401) throw new Error('unauth');
26+
2127
const text = await res.text();
2228
const data = JSON.parse(text);
2329
return data;
@@ -73,20 +79,18 @@ export function useRecipeComment({ cocktailId }: { cocktailId: number }) {
7379
staleTime: 30_000,
7480
});
7581

82+
const hasComment = comments.some((c: Comment) => c.userNickName === user?.nickname);
7683
const createMut = useMutation({
7784
mutationFn: (content: string) => {
7885
if (!user?.id) {
7986
toastInfo('로그인 후 이용 가능합니다.');
80-
return Promise.reject(new Error('unauth'));
87+
return Promise.resolve(null);
88+
} else if (hasComment) {
89+
toastInfo('댓글은 한 개만 작성 가능합니다.');
8190
}
8291
return postRecipeComment(cocktailId, content);
8392
},
8493
onSuccess: () => refetch(),
85-
onError: (e) => {
86-
if (e.message !== 'unauth') {
87-
toastInfo('댓글은 한개만 작성 가능합니다');
88-
}
89-
},
9094
});
9195

9296
const updateMut = useMutation({

src/domains/recipe/components/main/CocktailFilter.tsx

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import SelectBox from '@/shared/components/select-box/SelectBox';
2-
import { useQueryClient } from '@tanstack/react-query';
32
import { useRouter } from 'next/navigation';
43

54
interface Props {
@@ -12,14 +11,12 @@ function CocktailFilter({ cocktailsEA }: Props) {
1211
인기순: 'keeps',
1312
댓글순: 'comments',
1413
};
15-
const queryClient = useQueryClient();
14+
1615
const router = useRouter();
17-
const handleChange = async (selectTitle: string) => {
16+
17+
const handleChange = (selectTitle: string) => {
1818
const sortValue = sortMap[selectTitle as keyof typeof sortMap];
19-
queryClient.removeQueries({
20-
queryKey: ['cocktails', 'infinite'],
21-
exact: false,
22-
});
19+
2320
router.push(`?sortBy=${sortValue}`);
2421
};
2522

src/domains/recipe/components/main/Cocktails.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,11 @@ import { Sort } from '../../types/types';
1313

1414
function Cocktails() {
1515
const searchParams = useSearchParams();
16-
const sortBy = searchParams.get('sortBy') as Sort;
16+
const sortByParam = searchParams.get('sortBy') || 'recent';
1717
const [keyword, setKeyword] = useState('');
1818
const [input, setInput] = useState('');
1919

20+
const [sortBy, setSortBy] = useState<Sort>(sortByParam as Sort);
2021
const [alcoholStrengths, setAlcoholStrengths] = useState<string[]>([]);
2122
const [alcoholBaseTypes, setAlcoholBaseTypes] = useState<string[]>([]);
2223
const [cocktailTypes, setCocktailTypes] = useState<string[]>([]);
@@ -42,6 +43,10 @@ function Cocktails() {
4243
}
4344
}, [inView, hasNextPage, fetchNextPage]);
4445

46+
useEffect(() => {
47+
setSortBy(sortByParam as Sort);
48+
}, [sortByParam]);
49+
4550
const debounceKeyword = useMemo(() => debounce((v: string) => setKeyword(v), 300), []);
4651
const handleSearch = (v: string) => {
4752
setInput(v);

src/domains/recipe/types/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ export interface Cocktail {
55
cocktailImgUrl: string;
66
cocktailNameKo: string;
77
isKeep: boolean;
8+
keepCount?: number;
9+
commentCount?: number;
810
}
911

1012
export interface RecommendCocktail {

0 commit comments

Comments
 (0)