-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat: 좋아요 버튼 테두리 색상값 수정 * feat: 좋아요 버튼 분리 * feat: 아이콘 활성화 색상 상수 분리 * feat: FetchFeeds 타입 분리 * feat: 피드 좋아요 API * fix: 옵저버 위치 수정 * feat: 서버 성공 응답 확인 함수 * feat: date 관련 공용 함수 public api 처리 * feat: 좋아요 API 부수 기능 추가 * feat: isErrorResponse로 수정 * feat: 서버 데이터 처리 실패 시 쿼리 롤백 * feat: 좋아요 API 관련 조건부 추가 * feat: useLike public api 추가 * feat: 좋아요 취소 API * feat: 커스텀 훅 테스트를 위한 Wrapper 생성 * feat: 좋아요 상태 업데이트 함수 분리 * feat: 주석 오타 수정 * feat: 좋아요 및 좋아요 취소 API 분리 * style: 주석 수정 * test: 좋아요 및 좋아요 취소 API 호출 확인 * style: 주석 수정 * feat: useLike -> useLikes 훅스 명 변경 Closes #PW-297
- Loading branch information
Showing
29 changed files
with
274 additions
and
39 deletions.
There are no files selected for viewing
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { useLikes } from './useLikes'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
import { useMutation, useQueryClient } from '@tanstack/react-query'; | ||
|
||
import { requestLikeFeed, requestUnlikeFeed } from '@/shared/axios'; | ||
import { QUERY_KEYS } from '@/shared/consts'; | ||
import { isErrorResponse } from '@/shared/utils'; | ||
|
||
import { FeedsQueryData } from '../consts'; | ||
import { updateLikeStatusInFeeds } from '../lib'; | ||
|
||
export const useLikes = (feedId: number, isLiked: boolean) => { | ||
const queryClient = useQueryClient(); | ||
|
||
const { mutate: handleLikeFeed, isPending } = useMutation({ | ||
mutationFn: () => | ||
isLiked ? requestUnlikeFeed(feedId) : requestLikeFeed(feedId), | ||
// mutate가 호출되면 ✨낙관적 업데이트를 위해 onMutate를 실행 | ||
onMutate: async () => { | ||
// 진행중인 refetch가 있다면 취소시킨다. | ||
await queryClient.cancelQueries({ | ||
queryKey: [QUERY_KEYS.feeds], | ||
}); | ||
|
||
// 이전 쿼리값의 스냅샷 | ||
const previousQueryData = queryClient.getQueryData<FeedsQueryData>([ | ||
QUERY_KEYS.feeds, | ||
]); | ||
|
||
if (!previousQueryData) return; | ||
|
||
// 업데이트 될 쿼리값 | ||
const updatedQueryData = updateLikeStatusInFeeds( | ||
previousQueryData, | ||
feedId, | ||
); | ||
|
||
// setQueryData 함수를 사용해 newTodo로 Optimistic Update를 실시한다. | ||
await queryClient.setQueryData([QUERY_KEYS.feeds], updatedQueryData); | ||
|
||
return { previousQueryData }; | ||
}, | ||
onError: (_, __, context) => { | ||
// Network Error일 경우 이전 쿼리값으로 롤백 | ||
queryClient.setQueryData([QUERY_KEYS.feeds], context?.previousQueryData); | ||
}, | ||
onSuccess: (response, _, context) => { | ||
// Nextwork Success일 경우 실행 | ||
|
||
if (isErrorResponse(response)) { | ||
// 실패 시 이전 쿼리값으로 롤백 | ||
queryClient.setQueryData([QUERY_KEYS.feeds], context.previousQueryData); | ||
return; | ||
} | ||
|
||
// 성공 시 피드 아이디에 해당하는 피드를 무효화한다. | ||
queryClient.invalidateQueries({ queryKey: [QUERY_KEYS.feed, feedId] }); | ||
}, | ||
}); | ||
|
||
return { handleLikeFeed, isPending }; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import { FetchFeeds } from '@/shared/consts'; | ||
|
||
export interface FeedsQueryData { | ||
queryParams: number[]; | ||
pages: FetchFeeds[]; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { LikeButton } from './ui'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import { FeedsQueryData } from '../consts'; | ||
|
||
export function updateLikeStatusInFeeds( | ||
previousQueryData: FeedsQueryData, | ||
feedId: number, | ||
) { | ||
const { pages: previousPages } = previousQueryData; | ||
|
||
return { | ||
...previousQueryData, | ||
pages: previousPages.map((pageData) => { | ||
const { data } = pageData; | ||
const updateFeeds = data.feeds.map((feed) => | ||
feed.id === feedId | ||
? { | ||
...feed, | ||
likeCount: feed.isLiked ? feed.likeCount - 1 : feed.likeCount + 1, | ||
isLiked: !feed.isLiked, | ||
} | ||
: feed, | ||
); | ||
const updatedData = { ...data, feeds: updateFeeds }; | ||
|
||
return { ...pageData, data: updatedData }; | ||
}), | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import { renderHook, act, waitFor } from '@testing-library/react'; | ||
import { expect, test, vi } from 'vitest'; | ||
|
||
import * as likeModule from '@/shared/axios/like'; | ||
import { createQueryClientWrapper } from '@/shared/tests/setup'; | ||
|
||
import { useLikes } from '../api'; | ||
|
||
test('좋아요 상태가 아닐 때, 좋아요 버튼을 클릭하면 좋아요 요청이 발생한다.', async () => { | ||
// given | ||
// requestLikeFeed 함수를 스파이한다. | ||
const spy = vi.spyOn(likeModule, 'requestLikeFeed'); | ||
const { result } = renderHook(() => useLikes(1, false), { | ||
wrapper: createQueryClientWrapper(), | ||
}); | ||
|
||
// requestLikeFeed가 호출되지 않았는지 확인 | ||
await waitFor(() => expect(spy).not.toHaveBeenCalled()); | ||
|
||
// when | ||
// 좋아요 버튼 클릭 | ||
await act(async () => result.current.handleLikeFeed()); | ||
|
||
// then | ||
// requestLikeFeed가 호출되었는지 확인 | ||
await waitFor(() => expect(spy).toHaveBeenCalled()); | ||
}); | ||
|
||
test('좋아요 상태일 때, 좋아요 버튼을 클릭하면 좋아요 취소 요청이 발생한다.', async () => { | ||
// given | ||
const spy = vi.spyOn(likeModule, 'requestUnlikeFeed'); | ||
const { result } = renderHook(() => useLikes(1, true), { | ||
wrapper: createQueryClientWrapper(), | ||
}); | ||
|
||
await waitFor(() => expect(spy).not.toHaveBeenCalled()); | ||
|
||
// when | ||
await act(async () => result.current.handleLikeFeed()); | ||
|
||
// then | ||
await waitFor(() => expect(spy).toHaveBeenCalled()); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import { ICON_ACTIVE_COLOR } from '@/shared/consts'; | ||
import { Icon } from '@/shared/ui'; | ||
|
||
import { useLikes } from '../api'; | ||
|
||
interface LikeButtonProps { | ||
feedId: number; | ||
isLiked: boolean; | ||
} | ||
|
||
export const LikeButton: React.FC<LikeButtonProps> = ({ feedId, isLiked }) => { | ||
const { handleLikeFeed, isPending } = useLikes(feedId, isLiked); | ||
|
||
return ( | ||
<button | ||
className='icon icon-btn' | ||
onClick={() => handleLikeFeed()} | ||
disabled={isPending} | ||
> | ||
<Icon | ||
name='like' | ||
width='20' | ||
height='20' | ||
color={isLiked ? ICON_ACTIVE_COLOR : 'none'} | ||
/> | ||
</button> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { LikeButton } from './LikeButton'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { LikeButton } from './feed-main-like'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
export { axiosInstance } from './config/instance'; | ||
export * from './like'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import { axiosInstance } from '../config/instance'; | ||
|
||
/** | ||
* 좋아요 API | ||
* @param feedId 피드 아이디 | ||
* @returns 좋아요 상태 | ||
*/ | ||
export async function requestLikeFeed(feedId: number) { | ||
const { data } = await axiosInstance.put(`/feeds/${feedId}/likes`); | ||
|
||
return data; | ||
} | ||
|
||
/** | ||
* 좋아요 취소 API | ||
* @param feedId 피드 아이디 | ||
* @returns 좋아요 상태 | ||
*/ | ||
export async function requestUnlikeFeed(feedId: number) { | ||
const { data } = await axiosInstance.delete(`/feeds/${feedId}/likes`); | ||
|
||
return data; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export const ICON_ACTIVE_COLOR = '#00D5E1'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from './color'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
export { QUERY_KEYS } from './query-key/queryKeys'; | ||
export type * from './types'; | ||
export * from './color'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from './date'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
export * from './date/date'; | ||
export * from './date'; | ||
export * from './response'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from './response'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
interface Response { | ||
code: string; | ||
data: unknown; | ||
} | ||
|
||
export function isErrorResponse(response: Response) { | ||
return response.code !== '2000'; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.