Skip to content

Commit

Permalink
refactor: useExamLikeManager 공통 로직 분할
Browse files Browse the repository at this point in the history
  • Loading branch information
alstn113 committed Jan 21, 2025
1 parent fdc39dc commit 570b345
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 72 deletions.
13 changes: 5 additions & 8 deletions web/src/api/examAPI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,17 +68,14 @@ export const ExamAPI = {
return data;
},

like: async (examId: number, controller?: AbortController) => {
const { data } = await apiV1Client.post<void>(`/exams/${examId}/like`, {
signal: controller?.signal,
});
like: async (examId: number) => {
const { data } = await apiV1Client.post<void>(`/exams/${examId}/like`);

return data;
},

unlike: async (examId: number, controller?: AbortController) => {
const { data } = await apiV1Client.delete<void>(`/exams/${examId}/like`, {
signal: controller?.signal,
});
unlike: async (examId: number) => {
const { data } = await apiV1Client.delete<void>(`/exams/${examId}/like`);
return data;
},
};
Expand Down
107 changes: 43 additions & 64 deletions web/src/hooks/api/exam/useExamLikeManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import toast from 'react-hot-toast';
import useGetExamDetailSummary from '@/hooks/api/exam/useGetExamDetailSummary.ts';
import { ExamAPI, ExamDetailSummaryResponse } from '@/api/examAPI';

interface useExamLikeManagerProps {
interface UseExamLikeManagerProps {
examId: number;
initialIsLiked: boolean;
initialLikeCount: number;
Expand All @@ -15,7 +15,7 @@ const useExamLikeManager = ({
examId,
initialIsLiked,
initialLikeCount,
}: useExamLikeManagerProps) => {
}: UseExamLikeManagerProps) => {
const queryClient = useQueryClient();
const user = useUser();

Expand All @@ -24,88 +24,67 @@ const useExamLikeManager = ({

const debounceTimeout = useRef<number | null>(null);

const invalidateQueryDebounced = () => {
const debounceInvalidateQueries = () => {
if (debounceTimeout.current) {
clearTimeout(debounceTimeout.current);
}
debounceTimeout.current = setTimeout(async () => {
await queryClient.invalidateQueries({
debounceTimeout.current = setTimeout(() => {
queryClient.invalidateQueries({
queryKey: useGetExamDetailSummary.getKey(examId),
refetchType: 'all',
});
}, 300);
};

const { mutate: likeExam } = useMutation({
mutationFn: ExamAPI.like,
onMutate: async () => {
await queryClient.cancelQueries({
queryKey: useGetExamDetailSummary.getKey(examId),
});

const prevData = queryClient.getQueryData<ExamDetailSummaryResponse>(
useGetExamDetailSummary.getKey(examId),
);

setIsLiked(true);
setLikeCount(likeCount + 1);

return prevData;
},
onError: (_error, _variables, context) => {
if (context) {
queryClient.setQueryData(useGetExamDetailSummary.getKey(examId), context);
}

setIsLiked(false);
setLikeCount(likeCount - 1);
},
onSettled: async () => {
invalidateQueryDebounced();
},
});

const { mutate: unlikeExam } = useMutation({
mutationFn: ExamAPI.unlike,
onMutate: async () => {
await queryClient.cancelQueries({
queryKey: useGetExamDetailSummary.getKey(examId),
});

const prevData = queryClient.getQueryData<ExamDetailSummaryResponse>(
useGetExamDetailSummary.getKey(examId),
);

setIsLiked(false);
setLikeCount(likeCount - 1);

return prevData;
},
onError: (_error, _variables, context) => {
if (context) {
queryClient.setQueryData(useGetExamDetailSummary.getKey(examId), context);
}
const useLikeMutation = (
mutationFunction: (examId: number) => Promise<void>,
isLikeAction: boolean,
) => {
return useMutation({
mutationFn: mutationFunction,
onMutate: async () => {
await queryClient.cancelQueries({
queryKey: useGetExamDetailSummary.getKey(examId),
});

setIsLiked(isLikeAction);
setLikeCount((prevCount) => prevCount + (isLikeAction ? 1 : -1));

const previousData = queryClient.getQueryData<ExamDetailSummaryResponse>(
useGetExamDetailSummary.getKey(examId),
);

return { previousData };
},
onError: (_error, _variables, context) => {
if (context) {
queryClient.setQueryData(useGetExamDetailSummary.getKey(examId), context.previousData);
}

toast.error(`좋아요${isLikeAction ? '에' : ' 취소에'} 실패했습니다.`);

setIsLiked(!isLikeAction);
setLikeCount((prevCount) => prevCount - (isLikeAction ? 1 : -1));
},
onSettled: debounceInvalidateQueries,
});
};

setIsLiked(true);
setLikeCount(likeCount + 1);
},
onSettled: async () => {
invalidateQueryDebounced();
},
});
const { mutate: like } = useLikeMutation(ExamAPI.like, true);
const { mutate: unlike } = useLikeMutation(ExamAPI.unlike, false);

const toggleLike = () => {
if (!user) {
toast.error('로그인이 필요합니다.');
toast.error('좋아요를 누르려면 로그인이 필요합니다.');
return;
}

if (isLiked) {
unlikeExam(examId);
unlike(examId);
return;
}

likeExam(examId);
like(examId);
};

return {
Expand Down

0 comments on commit 570b345

Please sign in to comment.