Skip to content

Commit

Permalink
✨ 좋아요 싫어요 낙관적 업데이트 적용
Browse files Browse the repository at this point in the history
  • Loading branch information
khj0426 committed Oct 4, 2023
1 parent dd2e501 commit e570446
Show file tree
Hide file tree
Showing 4 changed files with 149 additions and 33 deletions.
4 changes: 2 additions & 2 deletions src/components/molcules/LikeDislikeCount/LikeDislikeCount.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import './index.scss'
export type LikeDislikeCountProps = {
like: number
dislike: number
onClickLike?: () => Promise<void>
onClickDisLike?: () => Promise<void>
onClickLike?: () => void
onClickDisLike?: () => void
initalState: 'like' | 'dislike' | 'init' | 'both'
}
export default function LikeDislikeCount({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ export default function LikeDislikeContainer({
onClickDisLike,
initalState,
}: LikeDislikeCountProps) {
const [loading, setLoading] = useState(false)
const { toggleDisLikeState, toggleLikeState, likeState } =
useLikeState(initalState)

Expand All @@ -34,28 +33,14 @@ export default function LikeDislikeContainer({
toggleLikeState()

if (onClickLike) {
setLoading(true)
onClickLike()
.then(() => {
setLoading(false)
})
.finally(() => {
setLoading(false)
})
}
}, [onClickLike, toggleLikeState])

const handleClickDisLike = useCallback(() => {
toggleDisLikeState()
if (onClickDisLike) {
setLoading(true)
onClickDisLike()
.then(() => {
setLoading(false)
})
.finally(() => {
setLoading(false)
})
}
}, [onClickDisLike, toggleDisLikeState])

Expand All @@ -64,11 +49,15 @@ export default function LikeDislikeContainer({
<>
<span className="like-container__likes">
<Image
src={likeState === 'like' ? Assets.ActiveLike : likeImage}
src={
likeState === 'like' || likeState === 'both'
? Assets.ActiveLike
: likeImage
}
alt="좋아요 이미지"
width={30}
height={30}
onClick={loading ? undefined : handleClickLike}
onClick={handleClickLike}
style={{
cursor: 'pointer',
}}
Expand All @@ -84,11 +73,15 @@ export default function LikeDislikeContainer({
/>
<span className="like-container__dislikes">
<Image
src={likeState === 'dislike' ? Assets.ActiveDisLike : disLikeImage}
src={
likeState === 'dislike' || likeState === 'both'
? Assets.ActiveDisLike
: disLikeImage
}
alt="싫어요 이미지"
width={30}
height={30}
onClick={loading ? undefined : handleClickDisLike}
onClick={handleClickDisLike}
style={{
cursor: 'pointer',
}}
Expand Down
36 changes: 24 additions & 12 deletions src/components/templates/PostDetailTemplate/PostDetailTemplate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import CommentInput from '@/components/organisms/CommentInput/CommentInput'
import CommentListContainer from '@/components/organisms/CommentList/CommentListContainer'
import { LikeDisLikeContainer } from '@/components/organisms/LikeDisLikeContainer'
import APP_PATH from '@/config/paths'
import useLike from '@/hooks/useLike'
import { useAuth } from '@/lib/contexts/authProvider'
import { useLikeQuery, useLikeMutate } from '@/queries/likes'
import { postNewComment } from '@/services/comment'
import Post from '@/types/post'
import './index.scss'
Expand All @@ -35,16 +35,24 @@ export function PostDetailTemplate({
const cachedCurrentUser = useMemo(() => currentUser, [currentUser])
const isEqualUser = cachedCurrentUser?._id === author._id

const { post, handleOnClickLike, handleOnClickDisLikeBtn, disLikePost } =
useLike({
initPost,
initDisLikePost: initDisLikeChannelPost,
})
const { data: postLike, isFetching: isLikePostFetching } = useLikeQuery(
initPost._id,
initPost,
)
const { data: postDisLike, isFetching: isDisLikePostFetching } = useLikeQuery(
initDisLikeChannelPost._id,
initDisLikeChannelPost,
)

const { likeMutation, disLikeMutation } = useLikeMutate({
initPost: initPost,
initDisLikePost: initDisLikeChannelPost,
})

const initLikeState = post?.likes.some(
const initLikeState = postLike?.likes.some(
(like) => like.user === currentUser?._id,
)
const initDisLikeState = disLikePost?.likes.some(
const initDisLikeState = postDisLike?.likes.some(
(dislike) => dislike.user === currentUser?._id,
)

Expand Down Expand Up @@ -89,10 +97,14 @@ export function PostDetailTemplate({

<LikeDisLikeContainer
initalState={initState}
like={post?.likes?.length || 0}
dislike={disLikePost?.likes?.length || 0}
onClickLike={handleOnClickLike}
onClickDisLike={handleOnClickDisLikeBtn}
like={postLike?.likes?.length || 0}
dislike={postDisLike?.likes?.length || 0}
onClickLike={() => {
!isLikePostFetching && likeMutation.mutate()
}}
onClickDisLike={() => {
!isDisLikePostFetching && disLikeMutation.mutate()
}}
/>
<CommentListContainer postId={postId} initComments={comment} />
{isLoggedIn && currentUser && (
Expand Down
111 changes: 111 additions & 0 deletions src/queries/likes/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import { useQueryClient, useMutation, useQuery } from '@tanstack/react-query'
import { useCurrentUser } from '@/hooks/useCurrentUser'
import { getPostDetail } from '@/services/post'
import { postLikeAction, postLikeCancelAction } from '@/services/post/like'
import type Post from '@/types/post'

type useLikeProps = {
initPost: Post
initDisLikePost: Post
}

export function useLikeQuery(postId: string, initPost: Post) {
return useQuery({
queryKey: ['post', postId],
queryFn: async () => {
const postDetail = await getPostDetail(postId)
const { post }: { post: Post } = postDetail
return post
},
initialData: initPost,
})
}

export function useLikeMutate({ initPost, initDisLikePost }: useLikeProps) {
const queryClient = useQueryClient()
const { currentUser } = useCurrentUser()

const likeMutation = useMutation(
async () => {
const prevPost = queryClient.getQueryData(['post', initPost._id]) as Post

if (prevPost.likes.some((like) => like.user === currentUser?._id)) {
const previousLikedPost = prevPost.likes.filter(
(like) => like.user === currentUser?._id,
)[0]._id

queryClient.setQueryData(['post', initPost._id], {
...prevPost,
likes: prevPost.likes.filter(
(like) => like.user !== currentUser?._id,
),
})
await postLikeCancelAction(previousLikedPost)
} else {
queryClient.setQueryData(['post', initPost._id], {
...prevPost,
likes: [
...prevPost.likes,
{
_id: '',
user: currentUser?._id,
},
],
})
await postLikeAction(initPost._id)
}
},
{
onMutate: async () => {
await queryClient.invalidateQueries(['post', initPost._id])
},
},
)

const disLikeMutation = useMutation(
async () => {
const prevDisLikePost = queryClient.getQueryData([
'post',
initDisLikePost._id,
]) as Post

if (
prevDisLikePost.likes.some((like) => like.user === currentUser?._id)
) {
const previousLikedPost = prevDisLikePost.likes.filter(
(like) => like.user === currentUser?._id,
)[0]._id

queryClient.setQueryData(['post', initDisLikePost._id], {
...prevDisLikePost,
likes: prevDisLikePost.likes.filter(
(like) => like.user !== currentUser?._id,
),
})
await postLikeCancelAction(previousLikedPost)
} else {
queryClient.setQueryData(['post', initDisLikePost._id], {
...prevDisLikePost,
likes: [
...prevDisLikePost.likes,
{
_id: '',
user: currentUser?._id,
},
],
})
await postLikeAction(initDisLikePost._id)
}
},
{
onMutate: async () => {
await queryClient.invalidateQueries(['post', initDisLikePost._id])
},
},
)

return {
likeMutation,
disLikeMutation,
}
}

0 comments on commit e570446

Please sign in to comment.